diff --git a/Cargo.lock b/Cargo.lock index 74f96983b3149..28068684362e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,11 +596,11 @@ dependencies = [ name = "clippy_dev" version = "0.0.1" dependencies = [ - "aho-corasick 0.7.20", + "aho-corasick 1.0.2", "clap", "indoc", "itertools", - "opener 0.5.2", + "opener", "shell-escape", "walkdir", ] @@ -610,7 +610,7 @@ name = "clippy_lints" version = "0.1.77" dependencies = [ "arrayvec", - "cargo_metadata 0.15.4", + "cargo_metadata 0.18.0", "clippy_config", "clippy_utils", "declare_clippy_lint", @@ -2351,7 +2351,7 @@ dependencies = [ "log", "memchr", "once_cell", - "opener 0.6.1", + "opener", "pathdiff", "pulldown-cmark", "regex", @@ -2626,16 +2626,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" -[[package]] -name = "opener" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005" -dependencies = [ - "bstr", - "winapi", -] - [[package]] name = "opener" version = "0.6.1" diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 7b781ba1e1121..aaeb1bb9bff82 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -3,8 +3,7 @@ use crate::ast::{self, LitKind, MetaItemLit, StrStyle}; use crate::token::{self, Token}; use rustc_lexer::unescape::{ - byte_from_char, unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit, - Mode, + byte_from_char, unescape_byte, unescape_char, unescape_mixed, unescape_unicode, MixedUnit, Mode, }; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; @@ -48,6 +47,9 @@ impl LitKind { return Err(LitError::InvalidSuffix); } + // For byte/char/string literals, chars and escapes have already been + // checked in the lexer (in `cook_lexer_literal`). So we can assume all + // chars and escapes are valid here. Ok(match kind { token::Bool => { assert!(symbol.is_bool_lit()); @@ -56,12 +58,12 @@ impl LitKind { token::Byte => { return unescape_byte(symbol.as_str()) .map(LitKind::Byte) - .map_err(|_| LitError::LexerError); + .map_err(|_| panic!("failed to unescape byte literal")); } token::Char => { return unescape_char(symbol.as_str()) .map(LitKind::Char) - .map_err(|_| LitError::LexerError); + .map_err(|_| panic!("failed to unescape char literal")); } // There are some valid suffixes for integer and float literals, @@ -77,26 +79,22 @@ impl LitKind { let s = symbol.as_str(); // Vanilla strings are so common we optimize for the common case where no chars // requiring special behaviour are present. - let symbol = if s.contains(['\\', '\r']) { + let symbol = if s.contains('\\') { let mut buf = String::with_capacity(s.len()); - let mut error = Ok(()); // Force-inlining here is aggressive but the closure is - // called on every char in the string, so it can be - // hot in programs with many long strings. - unescape_literal( + // called on every char in the string, so it can be hot in + // programs with many long strings containing escapes. + unescape_unicode( s, Mode::Str, &mut #[inline(always)] - |_, unescaped_char| match unescaped_char { + |_, c| match c { Ok(c) => buf.push(c), Err(err) => { - if err.is_fatal() { - error = Err(LitError::LexerError); - } + assert!(!err.is_fatal(), "failed to unescape string literal") } }, ); - error?; Symbol::intern(&buf) } else { symbol @@ -104,86 +102,46 @@ impl LitKind { LitKind::Str(symbol, ast::StrStyle::Cooked) } token::StrRaw(n) => { - // Raw strings have no escapes, so we only need to check for invalid chars, and we - // can reuse the symbol on success. - let mut error = Ok(()); - unescape_literal(symbol.as_str(), Mode::RawStr, &mut |_, unescaped_char| { - match unescaped_char { - Ok(_) => {} - Err(err) => { - if err.is_fatal() { - error = Err(LitError::LexerError); - } - } - } - }); - error?; + // Raw strings have no escapes so no work is needed here. LitKind::Str(symbol, ast::StrStyle::Raw(n)) } token::ByteStr => { let s = symbol.as_str(); let mut buf = Vec::with_capacity(s.len()); - let mut error = Ok(()); - unescape_literal(s, Mode::ByteStr, &mut |_, c| match c { + unescape_unicode(s, Mode::ByteStr, &mut |_, c| match c { Ok(c) => buf.push(byte_from_char(c)), Err(err) => { - if err.is_fatal() { - error = Err(LitError::LexerError); - } + assert!(!err.is_fatal(), "failed to unescape string literal") } }); - error?; LitKind::ByteStr(buf.into(), StrStyle::Cooked) } token::ByteStrRaw(n) => { - // Raw strings have no escapes, so we only need to check for invalid chars, and we - // can convert the symbol directly to a `Lrc` on success. - let s = symbol.as_str(); - let mut error = Ok(()); - unescape_literal(s, Mode::RawByteStr, &mut |_, c| match c { - Ok(_) => {} - Err(err) => { - if err.is_fatal() { - error = Err(LitError::LexerError); - } - } - }); - LitKind::ByteStr(s.to_owned().into_bytes().into(), StrStyle::Raw(n)) + // Raw strings have no escapes so we can convert the symbol + // directly to a `Lrc`. + let buf = symbol.as_str().to_owned().into_bytes(); + LitKind::ByteStr(buf.into(), StrStyle::Raw(n)) } token::CStr => { let s = symbol.as_str(); let mut buf = Vec::with_capacity(s.len()); - let mut error = Ok(()); - unescape_c_string(s, Mode::CStr, &mut |_span, c| match c { - Ok(CStrUnit::Byte(b)) => buf.push(b), - Ok(CStrUnit::Char(c)) => { + unescape_mixed(s, Mode::CStr, &mut |_span, c| match c { + Ok(MixedUnit::Char(c)) => { buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes()) } + Ok(MixedUnit::HighByte(b)) => buf.push(b), Err(err) => { - if err.is_fatal() { - error = Err(LitError::LexerError); - } + assert!(!err.is_fatal(), "failed to unescape C string literal") } }); - error?; buf.push(0); LitKind::CStr(buf.into(), StrStyle::Cooked) } token::CStrRaw(n) => { - // Raw strings have no escapes, so we only need to check for invalid chars, and we - // can convert the symbol directly to a `Lrc` on success. - let s = symbol.as_str(); - let mut error = Ok(()); - unescape_c_string(s, Mode::RawCStr, &mut |_, c| match c { - Ok(_) => {} - Err(err) => { - if err.is_fatal() { - error = Err(LitError::LexerError); - } - } - }); - error?; - let mut buf = s.to_owned().into_bytes(); + // Raw strings have no escapes so we can convert the symbol + // directly to a `Lrc` after appending the terminating NUL + // char. + let mut buf = symbol.as_str().to_owned().into_bytes(); buf.push(0); LitKind::CStr(buf.into(), StrStyle::Raw(n)) } diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch index be29ae09bcfc6..271ca12eabbcd 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch @@ -21,7 +21,7 @@ index 897a5e9..331f66f 100644 -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(int_roundings)] - #![feature(slice_group_by)] + #![feature(split_array)] diff --git a/atomic.rs b/atomic.rs index b735957..ea728b6 100644 --- a/atomic.rs diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index f574e81e90513..781186764fa32 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -373,7 +373,9 @@ impl<'a> StripUnconfigured<'a> { /// If attributes are not allowed on expressions, emit an error for `attr` #[instrument(level = "trace", skip(self))] pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { - if self.features.is_some_and(|features| !features.stmt_expr_attributes) { + if self.features.is_some_and(|features| !features.stmt_expr_attributes) + && !attr.span.allows_unstable(sym::stmt_expr_attributes) + { let mut err = feature_err( &self.sess, sym::stmt_expr_attributes, diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 0a632c4d12ad5..03d178eb266a4 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -80,12 +80,12 @@ impl EscapeError { } } -/// Takes a contents of a literal (without quotes) and produces a sequence of -/// escaped characters or errors. +/// Takes the contents of a unicode-only (non-mixed-utf8) literal (without +/// quotes) and produces a sequence of escaped characters or errors. /// /// Values are returned by invoking `callback`. For `Char` and `Byte` modes, /// the callback will be called exactly once. -pub fn unescape_literal(src: &str, mode: Mode, callback: &mut F) +pub fn unescape_unicode(src: &str, mode: Mode, callback: &mut F) where F: FnMut(Range, Result), { @@ -97,50 +97,63 @@ where } Str | ByteStr => unescape_non_raw_common(src, mode, callback), RawStr | RawByteStr => check_raw_common(src, mode, callback), - CStr | RawCStr => unreachable!(), + RawCStr => check_raw_common(src, mode, &mut |r, mut result| { + if let Ok('\0') = result { + result = Err(EscapeError::NulInCStr); + } + callback(r, result) + }), + CStr => unreachable!(), } } -/// A unit within CStr. Must not be a nul character. -pub enum CStrUnit { - Byte(u8), +/// Used for mixed utf8 string literals, i.e. those that allow both unicode +/// chars and high bytes. +pub enum MixedUnit { + /// Used for ASCII chars (written directly or via `\x00`..`\x7f` escapes) + /// and Unicode chars (written directly or via `\u` escapes). + /// + /// For example, if '¥' appears in a string it is represented here as + /// `MixedUnit::Char('¥')`, and it will be appended to the relevant byte + /// string as the two-byte UTF-8 sequence `[0xc2, 0xa5]` Char(char), + + /// Used for high bytes (`\x80`..`\xff`). + /// + /// For example, if `\xa5` appears in a string it is represented here as + /// `MixedUnit::HighByte(0xa5)`, and it will be appended to the relevant + /// byte string as the single byte `0xa5`. + HighByte(u8), } -impl From for CStrUnit { - fn from(value: u8) -> Self { - CStrUnit::Byte(value) +impl From for MixedUnit { + fn from(c: char) -> Self { + MixedUnit::Char(c) } } -impl From for CStrUnit { - fn from(value: char) -> Self { - CStrUnit::Char(value) +impl From for MixedUnit { + fn from(n: u8) -> Self { + if n.is_ascii() { MixedUnit::Char(n as char) } else { MixedUnit::HighByte(n) } } } -pub fn unescape_c_string(src: &str, mode: Mode, callback: &mut F) +/// Takes the contents of a mixed-utf8 literal (without quotes) and produces +/// a sequence of escaped characters or errors. +/// +/// Values are returned by invoking `callback`. +pub fn unescape_mixed(src: &str, mode: Mode, callback: &mut F) where - F: FnMut(Range, Result), + F: FnMut(Range, Result), { match mode { - CStr => { - unescape_non_raw_common(src, mode, &mut |r, mut result| { - if let Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) = result { - result = Err(EscapeError::NulInCStr); - } - callback(r, result) - }); - } - RawCStr => { - check_raw_common(src, mode, &mut |r, mut result| { - if let Ok('\0') = result { - result = Err(EscapeError::NulInCStr); - } - callback(r, result.map(CStrUnit::Char)) - }); - } - Char | Byte | Str | RawStr | ByteStr | RawByteStr => unreachable!(), + CStr => unescape_non_raw_common(src, mode, &mut |r, mut result| { + if let Ok(MixedUnit::Char('\0')) = result { + result = Err(EscapeError::NulInCStr); + } + callback(r, result) + }), + Char | Byte | Str | RawStr | ByteStr | RawByteStr | RawCStr => unreachable!(), } } @@ -181,29 +194,29 @@ impl Mode { } } - /// Non-byte literals should have `\xXX` escapes that are within the ASCII range. - fn ascii_escapes_should_be_ascii(self) -> bool { + /// Are `\x80`..`\xff` allowed? + fn allow_high_bytes(self) -> bool { match self { - Char | Str => true, - Byte | ByteStr | CStr => false, + Char | Str => false, + Byte | ByteStr | CStr => true, RawStr | RawByteStr | RawCStr => unreachable!(), } } - /// Whether characters within the literal must be within the ASCII range. + /// Are unicode (non-ASCII) chars allowed? #[inline] - fn chars_should_be_ascii(self) -> bool { + fn allow_unicode_chars(self) -> bool { match self { - Byte | ByteStr | RawByteStr => true, - Char | Str | RawStr | CStr | RawCStr => false, + Byte | ByteStr | RawByteStr => false, + Char | Str | RawStr | CStr | RawCStr => true, } } - /// Byte literals do not allow unicode escape. - fn is_unicode_escape_disallowed(self) -> bool { + /// Are unicode escapes (`\u`) allowed? + fn allow_unicode_escapes(self) -> bool { match self { - Byte | ByteStr => true, - Char | Str | CStr => false, + Byte | ByteStr => false, + Char | Str | CStr => true, RawByteStr | RawStr | RawCStr => unreachable!(), } } @@ -217,20 +230,19 @@ impl Mode { } } -fn scan_escape + From>( +fn scan_escape + From>( chars: &mut Chars<'_>, mode: Mode, ) -> Result { // Previous character was '\\', unescape what follows. - let res = match chars.next().ok_or(EscapeError::LoneSlash)? { - '"' => b'"', - 'n' => b'\n', - 'r' => b'\r', - 't' => b'\t', - '\\' => b'\\', - '\'' => b'\'', - '0' => b'\0', - + let res: char = match chars.next().ok_or(EscapeError::LoneSlash)? { + '"' => '"', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + '\\' => '\\', + '\'' => '\'', + '0' => '\0', 'x' => { // Parse hexadecimal character code. @@ -240,25 +252,23 @@ fn scan_escape + From>( let lo = chars.next().ok_or(EscapeError::TooShortHexEscape)?; let lo = lo.to_digit(16).ok_or(EscapeError::InvalidCharInHexEscape)?; - let value = hi * 16 + lo; - - if mode.ascii_escapes_should_be_ascii() && !is_ascii(value) { - return Err(EscapeError::OutOfRangeHexEscape); - } + let value = (hi * 16 + lo) as u8; - value as u8 + return if !mode.allow_high_bytes() && !value.is_ascii() { + Err(EscapeError::OutOfRangeHexEscape) + } else { + // This may be a high byte, but that will only happen if `T` is + // `MixedUnit`, because of the `allow_high_bytes` check above. + Ok(T::from(value as u8)) + }; } - - 'u' => return scan_unicode(chars, mode.is_unicode_escape_disallowed()).map(Into::into), + 'u' => return scan_unicode(chars, mode.allow_unicode_escapes()).map(T::from), _ => return Err(EscapeError::InvalidEscape), }; - Ok(res.into()) + Ok(T::from(res)) } -fn scan_unicode( - chars: &mut Chars<'_>, - is_unicode_escape_disallowed: bool, -) -> Result { +fn scan_unicode(chars: &mut Chars<'_>, allow_unicode_escapes: bool) -> Result { // We've parsed '\u', now we have to parse '{..}'. if chars.next() != Some('{') { @@ -286,7 +296,7 @@ fn scan_unicode( // Incorrect syntax has higher priority for error reporting // than unallowed value for a literal. - if is_unicode_escape_disallowed { + if !allow_unicode_escapes { return Err(EscapeError::UnicodeEscapeInByte); } @@ -312,12 +322,8 @@ fn scan_unicode( } #[inline] -fn ascii_check(c: char, chars_should_be_ascii: bool) -> Result { - if chars_should_be_ascii && !c.is_ascii() { - Err(EscapeError::NonAsciiCharInByte) - } else { - Ok(c) - } +fn ascii_check(c: char, allow_unicode_chars: bool) -> Result { + if allow_unicode_chars || c.is_ascii() { Ok(c) } else { Err(EscapeError::NonAsciiCharInByte) } } fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result { @@ -326,7 +332,7 @@ fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result scan_escape(chars, mode), '\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar), '\r' => Err(EscapeError::BareCarriageReturn), - _ => ascii_check(c, mode.chars_should_be_ascii()), + _ => ascii_check(c, mode.allow_unicode_chars()), }?; if chars.next().is_some() { return Err(EscapeError::MoreThanOneChar); @@ -336,12 +342,12 @@ fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result + From>(src: &str, mode: Mode, callback: &mut F) +fn unescape_non_raw_common + From>(src: &str, mode: Mode, callback: &mut F) where F: FnMut(Range, Result), { let mut chars = src.chars(); - let chars_should_be_ascii = mode.chars_should_be_ascii(); // get this outside the loop + let allow_unicode_chars = mode.allow_unicode_chars(); // get this outside the loop // The `start` and `end` computation here is complicated because // `skip_ascii_whitespace` makes us to skip over chars without counting @@ -366,7 +372,7 @@ where } '"' => Err(EscapeError::EscapeOnlyChar), '\r' => Err(EscapeError::BareCarriageReturn), - _ => ascii_check(c, chars_should_be_ascii).map(Into::into), + _ => ascii_check(c, allow_unicode_chars).map(T::from), }; let end = src.len() - chars.as_str().len(); callback(start..end, res); @@ -408,7 +414,7 @@ where F: FnMut(Range, Result), { let mut chars = src.chars(); - let chars_should_be_ascii = mode.chars_should_be_ascii(); // get this outside the loop + let allow_unicode_chars = mode.allow_unicode_chars(); // get this outside the loop // The `start` and `end` computation here matches the one in // `unescape_non_raw_common` for consistency, even though this function @@ -417,7 +423,7 @@ where let start = src.len() - chars.as_str().len() - c.len_utf8(); let res = match c { '\r' => Err(EscapeError::BareCarriageReturnInRawString), - _ => ascii_check(c, chars_should_be_ascii), + _ => ascii_check(c, allow_unicode_chars), }; let end = src.len() - chars.as_str().len(); callback(start..end, res); @@ -430,7 +436,3 @@ pub fn byte_from_char(c: char) -> u8 { debug_assert!(res <= u8::MAX as u32, "guaranteed because of ByteStr"); res as u8 } - -fn is_ascii(x: u32) -> bool { - x <= 0x7F -} diff --git a/compiler/rustc_lexer/src/unescape/tests.rs b/compiler/rustc_lexer/src/unescape/tests.rs index 1c25b03fdb22e..5b99495f47581 100644 --- a/compiler/rustc_lexer/src/unescape/tests.rs +++ b/compiler/rustc_lexer/src/unescape/tests.rs @@ -100,7 +100,7 @@ fn test_unescape_char_good() { fn test_unescape_str_warn() { fn check(literal: &str, expected: &[(Range, Result)]) { let mut unescaped = Vec::with_capacity(literal.len()); - unescape_literal(literal, Mode::Str, &mut |range, res| unescaped.push((range, res))); + unescape_unicode(literal, Mode::Str, &mut |range, res| unescaped.push((range, res))); assert_eq!(unescaped, expected); } @@ -124,7 +124,7 @@ fn test_unescape_str_warn() { fn test_unescape_str_good() { fn check(literal_text: &str, expected: &str) { let mut buf = Ok(String::with_capacity(literal_text.len())); - unescape_literal(literal_text, Mode::Str, &mut |range, c| { + unescape_unicode(literal_text, Mode::Str, &mut |range, c| { if let Ok(b) = &mut buf { match c { Ok(c) => b.push(c), @@ -241,7 +241,7 @@ fn test_unescape_byte_good() { fn test_unescape_byte_str_good() { fn check(literal_text: &str, expected: &[u8]) { let mut buf = Ok(Vec::with_capacity(literal_text.len())); - unescape_literal(literal_text, Mode::ByteStr, &mut |range, c| { + unescape_unicode(literal_text, Mode::ByteStr, &mut |range, c| { if let Ok(b) = &mut buf { match c { Ok(c) => b.push(byte_from_char(c)), @@ -264,7 +264,7 @@ fn test_unescape_byte_str_good() { fn test_unescape_raw_str() { fn check(literal: &str, expected: &[(Range, Result)]) { let mut unescaped = Vec::with_capacity(literal.len()); - unescape_literal(literal, Mode::RawStr, &mut |range, res| unescaped.push((range, res))); + unescape_unicode(literal, Mode::RawStr, &mut |range, res| unescaped.push((range, res))); assert_eq!(unescaped, expected); } @@ -276,7 +276,7 @@ fn test_unescape_raw_str() { fn test_unescape_raw_byte_str() { fn check(literal: &str, expected: &[(Range, Result)]) { let mut unescaped = Vec::with_capacity(literal.len()); - unescape_literal(literal, Mode::RawByteStr, &mut |range, res| unescaped.push((range, res))); + unescape_unicode(literal, Mode::RawByteStr, &mut |range, res| unescaped.push((range, res))); assert_eq!(unescaped, expected); } diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index f64509fe8bc67..bed5d3c80c046 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -4,7 +4,7 @@ use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{ self, fold::BottomUpFolder, print::TraitPredPrintModifiersAndPath, Ty, TypeFoldable, }; -use rustc_span::Span; +use rustc_span::{symbol::kw, Span}; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -96,6 +96,17 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { continue; } + // HACK: `async fn() -> Self` in traits is "ok"... + // This is not really that great, but it's similar to why the `-> Self` + // return type is well-formed in traits even when `Self` isn't sized. + if let ty::Param(param_ty) = *proj_term.kind() + && param_ty.name == kw::SelfUpper + && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(_)) + && opaque.in_trait + { + continue; + } + let proj_ty = Ty::new_projection(cx.tcx, proj.projection_ty.def_id, proj.projection_ty.args); // For every instance of the projection type in the bounds, diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index d7ecf577ed676..20ec4a300c1f8 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -400,7 +400,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { .with_code(error_code!(E0762)) .emit() } - self.cook_quoted(token::Char, Mode::Char, start, end, 1, 1) // ' ' + self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' ' } rustc_lexer::LiteralKind::Byte { terminated } => { if !terminated { @@ -412,7 +412,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { .with_code(error_code!(E0763)) .emit() } - self.cook_quoted(token::Byte, Mode::Byte, start, end, 2, 1) // b' ' + self.cook_unicode(token::Byte, Mode::Byte, start, end, 2, 1) // b' ' } rustc_lexer::LiteralKind::Str { terminated } => { if !terminated { @@ -424,7 +424,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { .with_code(error_code!(E0765)) .emit() } - self.cook_quoted(token::Str, Mode::Str, start, end, 1, 1) // " " + self.cook_unicode(token::Str, Mode::Str, start, end, 1, 1) // " " } rustc_lexer::LiteralKind::ByteStr { terminated } => { if !terminated { @@ -436,7 +436,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { .with_code(error_code!(E0766)) .emit() } - self.cook_quoted(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" " + self.cook_unicode(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" " } rustc_lexer::LiteralKind::CStr { terminated } => { if !terminated { @@ -448,13 +448,13 @@ impl<'sess, 'src> StringReader<'sess, 'src> { .with_code(error_code!(E0767)) .emit() } - self.cook_c_string(token::CStr, Mode::CStr, start, end, 2, 1) // c" " + self.cook_mixed(token::CStr, Mode::CStr, start, end, 2, 1) // c" " } rustc_lexer::LiteralKind::RawStr { n_hashes } => { if let Some(n_hashes) = n_hashes { let n = u32::from(n_hashes); let kind = token::StrRaw(n_hashes); - self.cook_quoted(kind, Mode::RawStr, start, end, 2 + n, 1 + n) // r##" "## + self.cook_unicode(kind, Mode::RawStr, start, end, 2 + n, 1 + n) // r##" "## } else { self.report_raw_str_error(start, 1); } @@ -463,7 +463,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { if let Some(n_hashes) = n_hashes { let n = u32::from(n_hashes); let kind = token::ByteStrRaw(n_hashes); - self.cook_quoted(kind, Mode::RawByteStr, start, end, 3 + n, 1 + n) // br##" "## + self.cook_unicode(kind, Mode::RawByteStr, start, end, 3 + n, 1 + n) // br##" "## } else { self.report_raw_str_error(start, 2); } @@ -472,7 +472,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { if let Some(n_hashes) = n_hashes { let n = u32::from(n_hashes); let kind = token::CStrRaw(n_hashes); - self.cook_c_string(kind, Mode::RawCStr, start, end, 3 + n, 1 + n) // cr##" "## + self.cook_unicode(kind, Mode::RawCStr, start, end, 3 + n, 1 + n) // cr##" "## } else { self.report_raw_str_error(start, 2); } @@ -735,7 +735,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { } } - fn cook_quoted( + fn cook_unicode( &self, kind: token::LitKind, mode: Mode, @@ -745,13 +745,13 @@ impl<'sess, 'src> StringReader<'sess, 'src> { postfix_len: u32, ) -> (token::LitKind, Symbol) { self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| { - unescape::unescape_literal(src, mode, &mut |span, result| { + unescape::unescape_unicode(src, mode, &mut |span, result| { callback(span, result.map(drop)) }) }) } - fn cook_c_string( + fn cook_mixed( &self, kind: token::LitKind, mode: Mode, @@ -761,7 +761,7 @@ impl<'sess, 'src> StringReader<'sess, 'src> { postfix_len: u32, ) -> (token::LitKind, Symbol) { self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| { - unescape::unescape_c_string(src, mode, &mut |span, result| { + unescape::unescape_mixed(src, mode, &mut |span, result| { callback(span, result.map(drop)) }) }) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 625764876a6b6..d76ee161da6fd 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -1056,7 +1056,7 @@ fn find_width_map_from_snippet( fn unescape_string(string: &str) -> Option { let mut buf = string::String::new(); let mut ok = true; - unescape::unescape_literal(string, unescape::Mode::Str, &mut |_, unescaped_char| { + unescape::unescape_unicode(string, unescape::Mode::Str, &mut |_, unescaped_char| { match unescaped_char { Ok(c) => buf.push(c), Err(_) => ok = false, diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d3909ce3a1c1c..9be286744351f 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -917,7 +917,7 @@ impl<'tcx> DeadVisitor<'tcx> { return; } dead_codes.sort_by_key(|v| v.level); - for group in dead_codes[..].group_by(|a, b| a.level == b.level) { + for group in dead_codes[..].chunk_by(|a, b| a.level == b.level) { self.lint_at_single_level(&group, participle, Some(def_id), report_on); } } diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 050a90efbdf6f..862b76b1f6048 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -11,7 +11,6 @@ #![feature(let_chains)] #![feature(map_try_insert)] #![feature(min_specialization)] -#![feature(slice_group_by)] #![feature(try_blocks)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9087ae00d1128..a64b06e70d6bc 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -22,7 +22,6 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, CRATE_DEF_ID}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, PatKind}; -use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::query::Providers; use rustc_middle::ty::GenericArgs; @@ -34,9 +33,9 @@ use rustc_span::hygiene::Transparency; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; +use std::fmt; use std::marker::PhantomData; use std::ops::ControlFlow; -use std::{fmt, mem}; use errors::{ FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface, @@ -933,7 +932,6 @@ impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> { struct NamePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, - current_item: LocalDefId, } impl<'tcx> NamePrivacyVisitor<'tcx> { @@ -949,6 +947,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { // Checks that a field in a struct constructor (expression or pattern) is accessible. fn check_field( &mut self, + hir_id: hir::HirId, // ID of the field use use_ctxt: Span, // syntax context of the field name at the use site span: Span, // span of the field pattern, e.g., `x: 0` def: ty::AdtDef<'tcx>, // definition of the struct or enum @@ -961,7 +960,6 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { // definition of the field let ident = Ident::new(kw::Empty, use_ctxt); - let hir_id = self.tcx.local_def_id_to_hir_id(self.current_item); let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id).1; if !field.vis.is_accessible_from(def_id, self.tcx) { self.tcx.dcx().emit_err(FieldIsPrivate { @@ -980,33 +978,13 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { - type NestedFilter = nested_filter::All; - - /// We want to visit items in the context of their containing - /// module and so forth, so supply a crate for doing a deep walk. - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_mod(&mut self, _m: &'tcx hir::Mod<'tcx>, _s: Span, _n: hir::HirId) { - // Don't visit nested modules, since we run a separate visitor walk - // for each module in `effective_visibilities` - } - - fn visit_nested_body(&mut self, body: hir::BodyId) { + fn visit_nested_body(&mut self, body_id: hir::BodyId) { let old_maybe_typeck_results = - self.maybe_typeck_results.replace(self.tcx.typeck_body(body)); - let body = self.tcx.hir().body(body); - self.visit_body(body); + self.maybe_typeck_results.replace(self.tcx.typeck_body(body_id)); + self.visit_body(self.tcx.hir().body(body_id)); self.maybe_typeck_results = old_maybe_typeck_results; } - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - let orig_current_item = mem::replace(&mut self.current_item, item.owner_id.def_id); - intravisit::walk_item(self, item); - self.current_item = orig_current_item; - } - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Struct(qpath, fields, ref base) = expr.kind { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); @@ -1020,17 +998,17 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { let field = fields .iter() .find(|f| self.typeck_results().field_index(f.hir_id) == vf_index); - let (use_ctxt, span) = match field { - Some(field) => (field.ident.span, field.span), - None => (base.span, base.span), + let (hir_id, use_ctxt, span) = match field { + Some(field) => (field.hir_id, field.ident.span, field.span), + None => (base.hir_id, base.span, base.span), }; - self.check_field(use_ctxt, span, adt, variant_field, true); + self.check_field(hir_id, use_ctxt, span, adt, variant_field, true); } } else { for field in fields { - let use_ctxt = field.ident.span; + let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); let index = self.typeck_results().field_index(field.hir_id); - self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false); + self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false); } } } @@ -1044,9 +1022,9 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { let adt = self.typeck_results().pat_ty(pat).ty_adt_def().unwrap(); let variant = adt.variant_of_res(res); for field in fields { - let use_ctxt = field.ident.span; + let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); let index = self.typeck_results().field_index(field.hir_id); - self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false); + self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false); } } @@ -1741,17 +1719,12 @@ pub fn provide(providers: &mut Providers) { fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { // Check privacy of names not checked in previous compilation stages. - let mut visitor = NamePrivacyVisitor { - tcx, - maybe_typeck_results: None, - current_item: module_def_id.to_local_def_id(), - }; - let (module, span, hir_id) = tcx.hir().get_module(module_def_id); - - intravisit::walk_mod(&mut visitor, module, hir_id); + let mut visitor = NamePrivacyVisitor { tcx, maybe_typeck_results: None }; + tcx.hir().visit_item_likes_in_module(module_def_id, &mut visitor); // Check privacy of explicitly written types and traits as well as // inferred types of expressions and patterns. + let span = tcx.def_span(module_def_id); let mut visitor = TypePrivacyVisitor { tcx, module_def_id, maybe_typeck_results: None, span }; tcx.hir().visit_item_likes_in_module(module_def_id, &mut visitor); } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 6977681e5a397..2e32101840780 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -191,8 +191,7 @@ mod thin; #[fundamental] #[stable(feature = "rust1", since = "1.0.0")] // The declaration of the `Box` struct must be kept in sync with the -// `alloc::alloc::box_free` function or ICEs will happen. See the comment -// on `box_free` for more details. +// compiler or ICEs will happen. pub struct Box< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 02ecbe22b3e15..7d04a2964037f 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -149,7 +149,6 @@ #![feature(set_ptr_value)] #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] -#![feature(slice_group_by)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] #![feature(slice_range)] diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index aa3b7b7e1914b..4033f4eb068c6 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -51,14 +51,14 @@ pub use core::slice::{from_mut, from_ref}; pub use core::slice::{from_mut_ptr_range, from_ptr_range}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{from_raw_parts, from_raw_parts_mut}; +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +pub use core::slice::{ChunkBy, ChunkByMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{Chunks, Windows}; #[stable(feature = "chunks_exact", since = "1.31.0")] pub use core::slice::{ChunksExact, ChunksExactMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{ChunksMut, Split, SplitMut}; -#[unstable(feature = "slice_group_by", issue = "80552")] -pub use core::slice::{GroupBy, GroupByMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{Iter, IterMut}; #[stable(feature = "rchunks", since = "1.31.0")] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 6ca0dca1e9d13..a360c43094666 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2204,14 +2204,6 @@ impl Vec { assert_failed(at, self.len()); } - if at == 0 { - // the new vector can take over the original buffer and avoid the copy - return mem::replace( - self, - Vec::with_capacity_in(self.capacity(), self.allocator().clone()), - ); - } - let other_len = self.len - at; let mut other = Vec::with_capacity_in(other_len, self.allocator().clone()); diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 2dcfc6b4abfe9..ca17dab55b027 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -29,7 +29,6 @@ #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(round_char_boundary)] -#![feature(slice_group_by)] #![feature(slice_partition_dedup)] #![feature(string_remove_matches)] #![feature(const_btree_len)] diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index 784839a3ffa42..c0f7a11a93e12 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1614,10 +1614,10 @@ fn subslice_patterns() { } #[test] -fn test_group_by() { +fn test_chunk_by() { let slice = &[1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; - let mut iter = slice.group_by(|a, b| a == b); + let mut iter = slice.chunk_by(|a, b| a == b); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); assert_eq!(iter.next(), Some(&[2, 2, 2][..])); @@ -1625,7 +1625,7 @@ fn test_group_by() { assert_eq!(iter.next(), Some(&[0][..])); assert_eq!(iter.next(), None); - let mut iter = slice.group_by(|a, b| a == b); + let mut iter = slice.chunk_by(|a, b| a == b); assert_eq!(iter.next_back(), Some(&[0][..])); assert_eq!(iter.next_back(), Some(&[1][..])); assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); @@ -1633,7 +1633,7 @@ fn test_group_by() { assert_eq!(iter.next_back(), Some(&[1, 1, 1][..])); assert_eq!(iter.next_back(), None); - let mut iter = slice.group_by(|a, b| a == b); + let mut iter = slice.chunk_by(|a, b| a == b); assert_eq!(iter.next(), Some(&[1, 1, 1][..])); assert_eq!(iter.next_back(), Some(&[0][..])); assert_eq!(iter.next(), Some(&[3, 3][..])); @@ -1643,10 +1643,10 @@ fn test_group_by() { } #[test] -fn test_group_by_mut() { +fn test_chunk_by_mut() { let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; - let mut iter = slice.group_by_mut(|a, b| a == b); + let mut iter = slice.chunk_by_mut(|a, b| a == b); assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); assert_eq!(iter.next(), Some(&mut [3, 3][..])); assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); @@ -1654,7 +1654,7 @@ fn test_group_by_mut() { assert_eq!(iter.next(), Some(&mut [0][..])); assert_eq!(iter.next(), None); - let mut iter = slice.group_by_mut(|a, b| a == b); + let mut iter = slice.chunk_by_mut(|a, b| a == b); assert_eq!(iter.next_back(), Some(&mut [0][..])); assert_eq!(iter.next_back(), Some(&mut [1][..])); assert_eq!(iter.next_back(), Some(&mut [2, 2, 2][..])); @@ -1662,7 +1662,7 @@ fn test_group_by_mut() { assert_eq!(iter.next_back(), Some(&mut [1, 1, 1][..])); assert_eq!(iter.next_back(), None); - let mut iter = slice.group_by_mut(|a, b| a == b); + let mut iter = slice.chunk_by_mut(|a, b| a == b); assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); assert_eq!(iter.next_back(), Some(&mut [0][..])); assert_eq!(iter.next(), Some(&mut [3, 3][..])); diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index d5ae8cdac1f4f..38a68df79cc73 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -958,23 +958,35 @@ fn test_append() { #[test] fn test_split_off() { let mut vec = vec![1, 2, 3, 4, 5, 6]; + let orig_ptr = vec.as_ptr(); let orig_capacity = vec.capacity(); - let vec2 = vec.split_off(4); + + let split_off = vec.split_off(4); assert_eq!(vec, [1, 2, 3, 4]); - assert_eq!(vec2, [5, 6]); + assert_eq!(split_off, [5, 6]); assert_eq!(vec.capacity(), orig_capacity); + assert_eq!(vec.as_ptr(), orig_ptr); } #[test] fn test_split_off_take_all() { - let mut vec = vec![1, 2, 3, 4, 5, 6]; + // Allocate enough capacity that we can tell whether the split-off vector's + // capacity is based on its size, or (incorrectly) on the original capacity. + let mut vec = Vec::with_capacity(1000); + vec.extend([1, 2, 3, 4, 5, 6]); let orig_ptr = vec.as_ptr(); let orig_capacity = vec.capacity(); - let vec2 = vec.split_off(0); + + let split_off = vec.split_off(0); assert_eq!(vec, []); - assert_eq!(vec2, [1, 2, 3, 4, 5, 6]); + assert_eq!(split_off, [1, 2, 3, 4, 5, 6]); assert_eq!(vec.capacity(), orig_capacity); - assert_eq!(vec2.as_ptr(), orig_ptr); + assert_eq!(vec.as_ptr(), orig_ptr); + + // The split-off vector should be newly-allocated, and should not have + // stolen the original vector's allocation. + assert!(split_off.capacity() < orig_capacity); + assert_ne!(split_off.as_ptr(), orig_ptr); } #[test] diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 1c65475b81d70..2d4c7e78aea17 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -3248,26 +3248,26 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for IterMut<'a, T> { /// An iterator over slice in (non-overlapping) chunks separated by a predicate. /// -/// This struct is created by the [`group_by`] method on [slices]. +/// This struct is created by the [`chunk_by`] method on [slices]. /// -/// [`group_by`]: slice::group_by +/// [`chunk_by`]: slice::chunk_by /// [slices]: slice -#[unstable(feature = "slice_group_by", issue = "80552")] +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct GroupBy<'a, T: 'a, P> { +pub struct ChunkBy<'a, T: 'a, P> { slice: &'a [T], predicate: P, } -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a, P> GroupBy<'a, T, P> { +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a, P> ChunkBy<'a, T, P> { pub(super) fn new(slice: &'a [T], predicate: P) -> Self { - GroupBy { slice, predicate } + ChunkBy { slice, predicate } } } -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a, P> Iterator for GroupBy<'a, T, P> +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a, P> Iterator for ChunkBy<'a, T, P> where P: FnMut(&T, &T) -> bool, { @@ -3300,8 +3300,8 @@ where } } -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a, P> DoubleEndedIterator for GroupBy<'a, T, P> +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a, P> DoubleEndedIterator for ChunkBy<'a, T, P> where P: FnMut(&T, &T) -> bool, { @@ -3322,39 +3322,39 @@ where } } -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a, P> FusedIterator for GroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool {} +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a, P> FusedIterator for ChunkBy<'a, T, P> where P: FnMut(&T, &T) -> bool {} -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupBy<'a, T, P> { +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for ChunkBy<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("GroupBy").field("slice", &self.slice).finish() + f.debug_struct("ChunkBy").field("slice", &self.slice).finish() } } /// An iterator over slice in (non-overlapping) mutable chunks separated /// by a predicate. /// -/// This struct is created by the [`group_by_mut`] method on [slices]. +/// This struct is created by the [`chunk_by_mut`] method on [slices]. /// -/// [`group_by_mut`]: slice::group_by_mut +/// [`chunk_by_mut`]: slice::chunk_by_mut /// [slices]: slice -#[unstable(feature = "slice_group_by", issue = "80552")] +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct GroupByMut<'a, T: 'a, P> { +pub struct ChunkByMut<'a, T: 'a, P> { slice: &'a mut [T], predicate: P, } -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a, P> GroupByMut<'a, T, P> { +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a, P> ChunkByMut<'a, T, P> { pub(super) fn new(slice: &'a mut [T], predicate: P) -> Self { - GroupByMut { slice, predicate } + ChunkByMut { slice, predicate } } } -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a, P> Iterator for GroupByMut<'a, T, P> +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a, P> Iterator for ChunkByMut<'a, T, P> where P: FnMut(&T, &T) -> bool, { @@ -3388,8 +3388,8 @@ where } } -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a, P> DoubleEndedIterator for GroupByMut<'a, T, P> +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a, P> DoubleEndedIterator for ChunkByMut<'a, T, P> where P: FnMut(&T, &T) -> bool, { @@ -3411,12 +3411,12 @@ where } } -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a, P> FusedIterator for GroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool {} +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a, P> FusedIterator for ChunkByMut<'a, T, P> where P: FnMut(&T, &T) -> bool {} -#[unstable(feature = "slice_group_by", issue = "80552")] -impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupByMut<'a, T, P> { +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for ChunkByMut<'a, T, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("GroupByMut").field("slice", &self.slice).finish() + f.debug_struct("ChunkByMut").field("slice", &self.slice).finish() } } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 27dda478848c0..2d93ef6fbeb34 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -68,8 +68,8 @@ pub use iter::{ArrayChunks, ArrayChunksMut}; #[unstable(feature = "array_windows", issue = "75027")] pub use iter::ArrayWindows; -#[unstable(feature = "slice_group_by", issue = "80552")] -pub use iter::{GroupBy, GroupByMut}; +#[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] +pub use iter::{ChunkBy, ChunkByMut}; #[stable(feature = "split_inclusive", since = "1.51.0")] pub use iter::{SplitInclusive, SplitInclusiveMut}; @@ -1748,18 +1748,16 @@ impl [T] { /// Returns an iterator over the slice producing non-overlapping runs /// of elements using the predicate to separate them. /// - /// The predicate is called on two elements following themselves, - /// it means the predicate is called on `slice[0]` and `slice[1]` - /// then on `slice[1]` and `slice[2]` and so on. + /// The predicate is called for every pair of consecutive elements, + /// meaning that it is called on `slice[0]` and `slice[1]`, + /// followed by `slice[1]` and `slice[2]`, and so on. /// /// # Examples /// /// ``` - /// #![feature(slice_group_by)] - /// /// let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; /// - /// let mut iter = slice.group_by(|a, b| a == b); + /// let mut iter = slice.chunk_by(|a, b| a == b); /// /// assert_eq!(iter.next(), Some(&[1, 1, 1][..])); /// assert_eq!(iter.next(), Some(&[3, 3][..])); @@ -1770,41 +1768,37 @@ impl [T] { /// This method can be used to extract the sorted subslices: /// /// ``` - /// #![feature(slice_group_by)] - /// /// let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4]; /// - /// let mut iter = slice.group_by(|a, b| a <= b); + /// let mut iter = slice.chunk_by(|a, b| a <= b); /// /// assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..])); /// assert_eq!(iter.next(), Some(&[2, 3][..])); /// assert_eq!(iter.next(), Some(&[2, 3, 4][..])); /// assert_eq!(iter.next(), None); /// ``` - #[unstable(feature = "slice_group_by", issue = "80552")] + #[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] #[inline] - pub fn group_by(&self, pred: F) -> GroupBy<'_, T, F> + pub fn chunk_by(&self, pred: F) -> ChunkBy<'_, T, F> where F: FnMut(&T, &T) -> bool, { - GroupBy::new(self, pred) + ChunkBy::new(self, pred) } /// Returns an iterator over the slice producing non-overlapping mutable /// runs of elements using the predicate to separate them. /// - /// The predicate is called on two elements following themselves, - /// it means the predicate is called on `slice[0]` and `slice[1]` - /// then on `slice[1]` and `slice[2]` and so on. + /// The predicate is called for every pair of consecutive elements, + /// meaning that it is called on `slice[0]` and `slice[1]`, + /// followed by `slice[1]` and `slice[2]`, and so on. /// /// # Examples /// /// ``` - /// #![feature(slice_group_by)] - /// /// let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2]; /// - /// let mut iter = slice.group_by_mut(|a, b| a == b); + /// let mut iter = slice.chunk_by_mut(|a, b| a == b); /// /// assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); /// assert_eq!(iter.next(), Some(&mut [3, 3][..])); @@ -1815,24 +1809,22 @@ impl [T] { /// This method can be used to extract the sorted subslices: /// /// ``` - /// #![feature(slice_group_by)] - /// /// let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4]; /// - /// let mut iter = slice.group_by_mut(|a, b| a <= b); + /// let mut iter = slice.chunk_by_mut(|a, b| a <= b); /// /// assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..])); /// assert_eq!(iter.next(), Some(&mut [2, 3][..])); /// assert_eq!(iter.next(), Some(&mut [2, 3, 4][..])); /// assert_eq!(iter.next(), None); /// ``` - #[unstable(feature = "slice_group_by", issue = "80552")] + #[stable(feature = "slice_group_by", since = "CURRENT_RUSTC_VERSION")] #[inline] - pub fn group_by_mut(&mut self, pred: F) -> GroupByMut<'_, T, F> + pub fn chunk_by_mut(&mut self, pred: F) -> ChunkByMut<'_, T, F> where F: FnMut(&T, &T) -> bool, { - GroupByMut::new(self, pred) + ChunkByMut::new(self, pred) } /// Divides one slice into two at an index. diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 89d2b5ef09383..57ad2ad1af9a3 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -101,7 +101,6 @@ #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(int_roundings)] -#![feature(slice_group_by)] #![feature(split_array)] #![feature(strict_provenance)] #![feature(strict_provenance_atomic_ptr)] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index b63091deac27e..13cc0511e103e 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -816,12 +816,12 @@ impl Error { } } - /// Attempt to downgrade the inner error to `E` if any. + /// Attempt to downcast the inner error to `E` if any. /// /// If this [`Error`] was constructed via [`new`] then this function will /// attempt to perform downgrade on it, otherwise it will return [`Err`]. /// - /// If downgrade succeeds, it will return [`Ok`], otherwise it will also + /// If the downcast succeeds, it will return [`Ok`], otherwise it will also /// return [`Err`]. /// /// [`new`]: Error::new @@ -852,13 +852,39 @@ impl Error { /// impl From for E { /// fn from(err: io::Error) -> E { /// err.downcast::() - /// .map(|b| *b) /// .unwrap_or_else(E::Io) /// } /// } + /// + /// impl From for io::Error { + /// fn from(err: E) -> io::Error { + /// match err { + /// E::Io(io_error) => io_error, + /// e => io::Error::new(io::ErrorKind::Other, e), + /// } + /// } + /// } + /// + /// # fn main() { + /// let e = E::SomeOtherVariant; + /// // Convert it to an io::Error + /// let io_error = io::Error::from(e); + /// // Cast it back to the original variant + /// let e = E::from(io_error); + /// assert!(matches!(e, E::SomeOtherVariant)); + /// + /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); + /// // Convert it to E + /// let e = E::from(io_error); + /// // Cast it back to the original variant + /// let io_error = io::Error::from(e); + /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); + /// assert!(io_error.get_ref().is_none()); + /// assert!(io_error.raw_os_error().is_none()); + /// # } /// ``` #[unstable(feature = "io_error_downcast", issue = "99262")] - pub fn downcast(self) -> result::Result, Self> + pub fn downcast(self) -> result::Result where E: error::Error + Send + Sync + 'static, { @@ -872,7 +898,7 @@ impl Error { // And the compiler should be able to eliminate the branch // that produces `Err` here since b.error.is::() // returns true. - Ok(res.unwrap()) + Ok(*res.unwrap()) } repr_data => Err(Self { repr: Repr::new(repr_data) }), } diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 36d52aef03cb7..fc6db2825e811 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -157,7 +157,7 @@ impl error::Error for E {} fn test_std_io_error_downcast() { // Case 1: custom error, downcast succeeds let io_error = Error::new(ErrorKind::Other, Bojji(true)); - let e: Box = io_error.downcast().unwrap(); + let e: Bojji = io_error.downcast().unwrap(); assert!(e.0); // Case 2: custom error, downcast fails @@ -166,7 +166,7 @@ fn test_std_io_error_downcast() { // ensures that the custom error is intact assert_eq!(ErrorKind::Other, io_error.kind()); - let e: Box = io_error.downcast().unwrap(); + let e: Bojji = io_error.downcast().unwrap(); assert!(e.0); // Case 3: os error diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 4d32bbec914a7..5fa45ceeb39be 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5606,6 +5606,7 @@ Released 2018-09-13 [`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl +[`suspicious_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_open_options [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned @@ -5814,6 +5815,7 @@ Released 2018-09-13 [`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments [`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles +[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates [`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 3b62ae0524ab0..7f7aff92bf19b 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -212,7 +212,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** @@ -768,7 +768,19 @@ Additional dotfiles (files or directories starting with a dot) to allow * [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext) +## `allowed-duplicate-crates` +A list of crate names to allow duplicates of + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`multiple_crate_versions`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions) + + ## `enforce-iter-loop-reborrow` +Whether to recommend using implicit into iter for reborrowed values. + #### Example ```no_run let mut vec = vec![1, 2, 3]; @@ -793,7 +805,7 @@ for _ in &mut *rmvec {} ## `check-private-items` - +Whether to also run the listed lints on private items. **Default Value:** `false` @@ -806,9 +818,10 @@ for _ in &mut *rmvec {} ## `pub-underscore-fields-behavior` +Lint "public" fields in a struct that are prefixed with an underscore based on their +exported visibility, or whether they are marked as "pub". - -**Default Value:** `"PublicallyExported"` +**Default Value:** `"PubliclyExported"` --- **Affected lints:** diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 5477d9b83a72c..4e9ddbf8259d3 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -2,7 +2,9 @@ use crate::msrvs::Msrv; use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename}; use crate::ClippyConfiguration; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; use rustc_session::Session; +use rustc_span::edit_distance::edit_distance; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::de::{IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; @@ -26,7 +28,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", - "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", + "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", @@ -59,18 +61,25 @@ impl TryConf { #[derive(Debug)] struct ConfError { message: String, + suggestion: Option, span: Span, } impl ConfError { fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self { let span = error.span().unwrap_or(0..file.source_len.0 as usize); - Self::spanned(file, error.message(), span) + Self::spanned(file, error.message(), None, span) } - fn spanned(file: &SourceFile, message: impl Into, span: Range) -> Self { + fn spanned( + file: &SourceFile, + message: impl Into, + suggestion: Option, + span: Range, + ) -> Self { Self { message: message.into(), + suggestion, span: Span::new( file.start_pos + BytePos::from_usize(span.start), file.start_pos + BytePos::from_usize(span.end), @@ -147,16 +156,18 @@ macro_rules! define_Conf { match Field::deserialize(name.get_ref().as_str().into_deserializer()) { Err(e) => { let e: FieldError = e; - errors.push(ConfError::spanned(self.0, e.0, name.span())); + errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span())); } $(Ok(Field::$name) => { - $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), name.span()));)? + $(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(), value_span)), + 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()), name.span())), + Some(_) => { + errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); + } None => { $name = Some(value); // $new_conf is the same as one of the defined `$name`s, so @@ -165,7 +176,7 @@ macro_rules! define_Conf { Some(_) => errors.push(ConfError::spanned(self.0, concat!( "duplicate field `", stringify!($new_conf), "` (provided as `", stringify!($name), "`)" - ), name.span())), + ), None, name.span())), None => $new_conf = $name.clone(), })? }, @@ -523,7 +534,11 @@ define_Conf! { /// /// Additional dotfiles (files or directories starting with a dot) to allow (allowed_dotfiles: FxHashSet = FxHashSet::default()), - /// Lint: EXPLICIT_ITER_LOOP + /// Lint: MULTIPLE_CRATE_VERSIONS. + /// + /// A list of crate names to allow duplicates of + (allowed_duplicate_crates: FxHashSet = FxHashSet::default()), + /// Lint: EXPLICIT_ITER_LOOP. /// /// Whether to recommend using implicit into iter for reborrowed values. /// @@ -543,15 +558,15 @@ define_Conf! { /// for _ in &mut *rmvec {} /// ``` (enforce_iter_loop_reborrow: bool = false), - /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC + /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC. /// /// Whether to also run the listed lints on private items. (check_private_items: bool = false), - /// Lint: PUB_UNDERSCORE_FIELDS + /// Lint: PUB_UNDERSCORE_FIELDS. /// /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". - (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PublicallyExported), + (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported), } /// Search for the configuration file. @@ -669,10 +684,16 @@ impl Conf { // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { - sess.dcx().span_err( + let mut diag = sess.dcx().struct_span_err( error.span, format!("error reading Clippy's configuration file: {}", error.message), ); + + if let Some(sugg) = error.suggestion { + diag.span_suggestion(error.span, sugg.message, sugg.suggestion, Applicability::MaybeIncorrect); + } + + diag.emit(); } for warning in warnings { @@ -689,19 +710,31 @@ impl Conf { const SEPARATOR_WIDTH: usize = 4; #[derive(Debug)] -struct FieldError(String); +struct FieldError { + error: String, + suggestion: Option, +} + +#[derive(Debug)] +struct Suggestion { + message: &'static str, + suggestion: &'static str, +} impl std::error::Error for FieldError {} impl Display for FieldError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.pad(&self.0) + f.pad(&self.error) } } impl serde::de::Error for FieldError { fn custom(msg: T) -> Self { - Self(msg.to_string()) + Self { + error: msg.to_string(), + suggestion: None, + } } fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { @@ -723,7 +756,20 @@ impl serde::de::Error for FieldError { write!(msg, "{:SEPARATOR_WIDTH$}{field:column_width$}", " ").unwrap(); } } - Self(msg) + + let suggestion = expected + .iter() + .filter_map(|expected| { + let dist = edit_distance(field, expected, 4)?; + Some((dist, expected)) + }) + .min_by_key(|&(dist, _)| dist) + .map(|(_, suggestion)| Suggestion { + message: "perhaps you meant", + suggestion, + }); + + Self { error: msg, suggestion } } } diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index f5dcb16d670df..533e375a3104c 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -11,6 +11,7 @@ extern crate rustc_ast; extern crate rustc_data_structures; #[allow(unused_extern_crates)] extern crate rustc_driver; +extern crate rustc_errors; extern crate rustc_session; extern crate rustc_span; diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index baee09629ac44..435aa9244c522 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -129,6 +129,6 @@ unimplemented_serialize! { #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum PubUnderscoreFieldsBehaviour { - PublicallyExported, + PubliclyExported, AllPubFields, } diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index ce738e3f4ec7c..5ec67554e7d89 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -4,11 +4,11 @@ version = "0.0.1" edition = "2021" [dependencies] -aho-corasick = "0.7" +aho-corasick = "1.0" clap = "4.1.4" indoc = "1.0" itertools = "0.11" -opener = "0.5" +opener = "0.6" shell-escape = "0.1" walkdir = "2.3" diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 6b76a44debff7..2222abff7adfb 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -504,9 +504,8 @@ fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option(replacements.iter().map(|&(x, _)| x.as_bytes())) + .build(replacements.iter().map(|&(x, _)| x.as_bytes())) .unwrap(); let mut result = String::with_capacity(contents.len() + 1024); @@ -928,7 +927,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_literal(s, unescape::Mode::Str, &mut |range, ch| { + unescape::unescape_unicode(s, unescape::Mode::Str, &mut |range, ch| { if ch.is_ok() { res.push_str(&s[range]); } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 8cba35f3d871c..416e9a680dd73 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] arrayvec = { version = "0.7", default-features = false } -cargo_metadata = "0.15.3" +cargo_metadata = "0.18" clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs index 657d52d0e9e17..1102c7fb236b5 100644 --- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs +++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs @@ -14,10 +14,10 @@ declare_clippy_lint! { /// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`. /// /// ### Why is this bad? - /// `Arc` is an Atomic `RC` and guarantees that updates to the reference counter are - /// Atomic. This is useful in multiprocessing scenarios. To send an `Arc` across processes - /// and make use of the atomic ref counter, `T` must be [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E), - /// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc` + /// `Arc` is a thread-safe `Rc` and guarantees that updates to the reference counter + /// use atomic operations. To send an `Arc` across thread boundaries and + /// share ownership between multiple threads, `T` must be [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#thread-safety), + /// so either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc` /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs index 1417e230aee5f..ff4dffd06079f 100644 --- a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs +++ b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs @@ -67,6 +67,11 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { ); if let ExprKind::Block(block, _) = &cond.kind { + if !block.span.eq_ctxt(expr.span) { + // If the block comes from a macro, or as an argument to a macro, + // do not lint. + return; + } if block.rules == BlockCheckMode::DefaultBlock { if block.stmts.is_empty() { if let Some(ex) = &block.expr { diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs index fea6924d89e91..d8107f61f371c 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs @@ -6,6 +6,7 @@ mod wildcard_dependencies; use cargo_metadata::MetadataCommand; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_lint_allowed; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_session::impl_lint_pass; @@ -128,6 +129,8 @@ declare_clippy_lint! { /// ### Known problems /// Because this can be caused purely by the dependencies /// themselves, it's not always possible to fix this issue. + /// In those cases, you can allow that specific crate using + /// the `allowed_duplicate_crates` configuration option. /// /// ### Example /// ```toml @@ -163,6 +166,7 @@ declare_clippy_lint! { } pub struct Cargo { + pub allowed_duplicate_crates: FxHashSet, pub ignore_publish: bool, } @@ -208,7 +212,7 @@ impl LateLintPass<'_> for Cargo { { match MetadataCommand::new().exec() { Ok(metadata) => { - multiple_crate_versions::check(cx, &metadata); + multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates); }, Err(e) => { for lint in WITH_DEPS_LINTS { diff --git a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs index ec681adb7aef2..3f30a77fcfe47 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -3,27 +3,40 @@ use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId}; use clippy_utils::diagnostics::span_lint; use itertools::Itertools; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LOCAL_CRATE; use rustc_lint::LateContext; use rustc_span::DUMMY_SP; use super::MULTIPLE_CRATE_VERSIONS; -pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { +pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, allowed_duplicate_crates: &FxHashSet) { let local_name = cx.tcx.crate_name(LOCAL_CRATE); let mut packages = metadata.packages.clone(); packages.sort_by(|a, b| a.name.cmp(&b.name)); if let Some(resolve) = &metadata.resolve && let Some(local_id) = packages.iter().find_map(|p| { - if p.name == local_name.as_str() { + // p.name contains the original crate names with dashes intact + // local_name contains the crate name as a namespace, with the dashes converted to underscores + // the code below temporarily rectifies this discrepancy + if p.name + .as_bytes() + .iter() + .map(|b| if b == &b'-' { &b'_' } else { b }) + .eq(local_name.as_str().as_bytes()) + { Some(&p.id) } else { None } }) { - for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { + for (name, group) in &packages + .iter() + .filter(|p| !allowed_duplicate_crates.contains(&p.name)) + .group_by(|p| &p.name) + { let group: Vec<&Package> = group.collect(); if group.len() <= 1 { diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 20230106d5366..639edd8da3012 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -439,6 +439,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO, crate::methods::SUSPICIOUS_MAP_INFO, + crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO, crate::methods::TYPE_ID_ON_BOX_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs index 2472e2ee76f93..e617c19eff09b 100644 --- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::last_path_segment; use clippy_utils::source::snippet_with_context; +use clippy_utils::{last_path_segment, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -42,12 +42,14 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { && ty.span.ctxt() == ctxt { let mut applicability = Applicability::MachineApplicable; - let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability); + let Some(path) = std_or_core(cx) else { return }; + let path = format!("{path}::iter::empty"); + let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability, &path); span_lint_and_sugg( cx, DEFAULT_INSTEAD_OF_ITER_EMPTY, expr.span, - "`std::iter::empty()` is the more idiomatic way", + &format!("`{path}()` is the more idiomatic way"), "try", sugg, applicability, @@ -61,6 +63,7 @@ fn make_sugg( ty_path: &rustc_hir::QPath<'_>, ctxt: SyntaxContext, applicability: &mut Applicability, + path: &str, ) -> String { if let Some(last) = last_path_segment(ty_path).args && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg { @@ -69,10 +72,10 @@ fn make_sugg( }) { format!( - "std::iter::empty::<{}>()", + "{path}::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0 ) } else { - "std::iter::empty()".to_owned() + format!("{path}()") } } diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 712bc075650fa..c4437a3c4b339 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; -use clippy_utils::{get_parent_node, numeric_literal}; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; -use rustc_hir::{Block, Body, Expr, ExprKind, FnRetTy, HirId, ItemKind, Lit, Node, Stmt, StmtKind}; +use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; @@ -50,11 +50,11 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) { - matches!(item.kind, ItemKind::Const(..)) - } else { - false - }; + let hir = cx.tcx.hir(); + let is_parent_const = matches!( + hir.body_const_context(hir.body_owner_def_id(body.id())), + Some(ConstContext::Const { inline: false } | ConstContext::Static(_)) + ); let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); } diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index df596338b9500..6144ec7b3caca 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{is_lint_allowed, match_def_path, paths}; +use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; @@ -450,6 +450,7 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) && let Some(def_id) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) + && !has_non_exhaustive_attr(cx.tcx, *adt) && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[]) // If all of our fields implement `Eq`, we can implement `Eq` too 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 8b018220c1715..8dde4f227ed15 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 @@ -6,7 +6,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::HumanEmitter; -use rustc_errors::DiagCtxt; +use rustc_errors::{DiagCtxt, DiagnosticBuilder}; use rustc_lint::LateContext; use rustc_parse::maybe_new_parser_from_source_str; use rustc_parse::parser::ForceCollect; @@ -53,7 +53,7 @@ pub fn check( let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { Ok(p) => p, Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); + errs.into_iter().for_each(DiagnosticBuilder::cancel); return (false, test_attr_spans); }, }; diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index fa1f98ba01343..1933a00891b0b 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -6,8 +6,8 @@ use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, Visitor}; use rustc_hir::{ - GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, - TyKind, + FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, + PathSegment, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -197,10 +197,13 @@ fn convert_to_from( // fn into([mut] self) -> T -> fn into([mut] v: T) -> T // ~~~~ ~~~~ (self_ident.span, format!("val: {from}")), + ]; + + if let FnRetTy::Return(_) = sig.decl.output { // fn into(self) -> T -> fn into(self) -> Self // ~ ~~~~ - (sig.decl.output.span(), String::from("Self")), - ]; + suggestions.push((sig.decl.output.span(), String::from("Self"))); + } let mut finder = SelfFinder { cx, diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index fb7b82ec304ef..1127f00abde04 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -53,7 +53,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // List of spans to lint. (lint_span, first_span) let mut lint_spans = Vec::new(); - let Ok(impls) = cx.tcx.crate_inherent_impls(()) else { return }; + let Ok(impls) = cx.tcx.crate_inherent_impls(()) else { + return; + }; let inherent_impls = cx .tcx .with_stable_hashing_context(|hcx| impls.inherent_impls.to_sorted(&hcx, true)); diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index efdd392594977..feb4d188f3978 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -574,6 +574,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { warn_on_all_wildcard_imports, check_private_items, pub_underscore_fields_behavior, + ref allowed_duplicate_crates, blacklisted_names: _, cyclomatic_complexity_threshold: _, @@ -719,7 +720,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate)); store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)); store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); - store.register_late_pass(|_| Box::new(no_effect::NoEffect)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); store.register_late_pass(move |_| { @@ -947,6 +948,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, + allowed_duplicate_crates: allowed_duplicate_crates.clone(), }) }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index c245eaf1ab44d..920a887a6fd1b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( vec.span, "it looks like the same item is being pushed into this Vec", None, - &format!("try using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), + &format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), ); } diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index c22f76484d03d..fa4f4a47e38e2 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lin use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Expr, ExprKind}; @@ -128,6 +128,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' // check if replacement is mem::MaybeUninit::uninit().assume_init() && cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id) { + let Some(top_crate) = std_or_core(cx) else { return }; let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -136,7 +137,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' "replacing with `mem::MaybeUninit::uninit().assume_init()`", "consider using", format!( - "std::ptr::read({})", + "{top_crate}::ptr::read({})", snippet_with_applicability(cx, dest.span, "", &mut applicability) ), applicability, @@ -149,6 +150,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() { if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { + let Some(top_crate) = std_or_core(cx) else { return }; let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -157,7 +159,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' "replacing with `mem::uninitialized()`", "consider using", format!( - "std::ptr::read({})", + "{top_crate}::ptr::read({})", snippet_with_applicability(cx, dest.span, "", &mut applicability) ), applicability, @@ -184,14 +186,17 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< return; } if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) { + let Some(top_crate) = std_or_core(cx) else { return }; span_lint_and_then( cx, MEM_REPLACE_WITH_DEFAULT, expr_span, - "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", + &format!( + "replacing a value of type `T` with `T::default()` is better expressed using `{top_crate}::mem::take`" + ), |diag| { if !expr_span.from_expansion() { - let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); + let suggestion = format!("{top_crate}::mem::take({})", snippet(cx, dest.span, "")); diag.span_suggestion( expr_span, diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 70abe4891d985..4c7c56e7174a0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{get_expr_use_or_unification_node, is_no_std_crate, is_res_lang_ctor, path_res}; +use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -58,10 +58,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re return; } + let Some(top_crate) = std_or_core(cx) else { return }; if let Some(i) = item { let sugg = format!( - "{}::iter::once({}{})", - if is_no_std_crate(cx) { "core" } else { "std" }, + "{top_crate}::iter::once({}{})", iter_type.ref_prefix(), snippet(cx, i.span, "...") ); @@ -81,11 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re expr.span, &format!("`{method_name}` call on an empty collection"), "try", - if is_no_std_crate(cx) { - "core::iter::empty()".to_string() - } else { - "std::iter::empty()".to_string() - }, + format!("{top_crate}::iter::empty()"), Applicability::MaybeIncorrect, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs index 02f28779cf6ee..aa1ec60d434a5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs @@ -42,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a ) .span_suggestion( expr_span, - "if this is intentional, try using `Path::new` instead", + "if this is intentional, consider using `Path::new`", format!("PathBuf::from({arg_str})"), Applicability::Unspecified, ); 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 04bdbc1ea25fe..bf437db7e72ab 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 @@ -50,7 +50,7 @@ pub fn check( super::MANUAL_SATURATING_ARITHMETIC, expr.span, "manual saturating arithmetic", - &format!("try using `saturating_{arith}`"), + &format!("consider using `saturating_{arith}`"), format!( "{}.saturating_{arith}({})", snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), 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 f9f636bbbf71b..27e17b43b01cc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -113,9 +113,15 @@ fn handle_path( if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() && match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) { - // FIXME: It would be better to infer the type to check if it's copyable or not - // to suggest to use `.copied()` instead of `.cloned()` where applicable. - lint_path(cx, e.span, recv.span); + // 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()) + && ty.is_ref() + { + lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); + } } } @@ -139,17 +145,19 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { ); } -fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span) { +fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; + let replacement = if is_copy { "copied" } else { "cloned" }; + span_lint_and_sugg( cx, MAP_CLONE, replace, "you are explicitly cloning with `.map()`", - "consider calling the dedicated `cloned` method", + &format!("consider calling the dedicated `{replacement}` method"), format!( - "{}.cloned()", + "{}.{replacement}()", snippet_with_applicability(cx, root, "..", &mut applicability), ), applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 89ea3597dc0fd..03bcf108914a0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -2827,6 +2827,44 @@ declare_clippy_lint! { "nonsensical combination of options for opening a file" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the suspicious use of `OpenOptions::create()` + /// without an explicit `OpenOptions::truncate()`. + /// + /// ### Why is this bad? + /// `create()` alone will either create a new file or open an + /// existing file. If the file already exists, it will be + /// overwritten when written to, but the file will not be + /// truncated by default. + /// If less data is written to the file + /// than it already contains, the remainder of the file will + /// remain unchanged, and the end of the file will contain old + /// data. + /// In most cases, one should either use `create_new` to ensure + /// the file is created from scratch, or ensure `truncate` is + /// called so that the truncation behaviour is explicit. `truncate(true)` + /// will ensure the file is entirely overwritten with new data, whereas + /// `truncate(false)` will explicitely keep the default behavior. + /// + /// ### Example + /// ```rust,no_run + /// use std::fs::OpenOptions; + /// + /// OpenOptions::new().create(true); + /// ``` + /// Use instead: + /// ```rust,no_run + /// use std::fs::OpenOptions; + /// + /// OpenOptions::new().create(true).truncate(true); + /// ``` + #[clippy::version = "1.75.0"] + pub SUSPICIOUS_OPEN_OPTIONS, + suspicious, + "suspicious combination of options for opening a file" +} + declare_clippy_lint! { /// ### What it does ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) @@ -4033,6 +4071,7 @@ impl_lint_pass!(Methods => [ MAP_ERR_IGNORE, MUT_MUTEX_LOCK, NONSENSICAL_OPEN_OPTIONS, + SUSPICIOUS_OPEN_OPTIONS, PATH_BUF_PUSH_OVERWRITE, RANGE_ZIP_WITH_LEN, REPEAT_ONCE, diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index 65c986dcaccea..77484ab91a9d7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -1,46 +1,74 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use rustc_data_structures::fx::FxHashMap; + +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::{match_any_def_paths, paths}; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty::Ty; use rustc_span::source_map::Spanned; use rustc_span::{sym, Span}; -use super::NONSENSICAL_OPEN_OPTIONS; +use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; + +fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS) +} pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::FsOpenOptions) + && is_open_options(cx, cx.tcx.type_of(impl_id).instantiate_identity()) { let mut options = Vec::new(); - get_open_options(cx, recv, &mut options); - check_open_options(cx, &options, e.span); + if get_open_options(cx, recv, &mut options) { + check_open_options(cx, &options, e.span); + } } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Eq, PartialEq, Clone, Debug)] enum Argument { - True, - False, + Set(bool), Unknown, } -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq, Hash, Clone)] enum OpenOption { - Write, + Append, + Create, + CreateNew, Read, Truncate, - Create, - Append, + Write, +} +impl std::fmt::Display for OpenOption { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + OpenOption::Append => write!(f, "append"), + OpenOption::Create => write!(f, "create"), + OpenOption::CreateNew => write!(f, "create_new"), + OpenOption::Read => write!(f, "read"), + OpenOption::Truncate => write!(f, "truncate"), + OpenOption::Write => write!(f, "write"), + } + } } -fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) { - if let ExprKind::MethodCall(path, receiver, arguments, _) = argument.kind { +/// Collects information about a method call chain on `OpenOptions`. +/// Returns false if an unexpected expression kind was found "on the way", +/// and linting should then be avoided. +fn get_open_options( + cx: &LateContext<'_>, + argument: &Expr<'_>, + options: &mut Vec<(OpenOption, Argument, Span)>, +) -> bool { + if let ExprKind::MethodCall(path, receiver, arguments, span) = argument.kind { let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); // Only proceed if this is a call on some object of type std::fs::OpenOptions - if is_type_diagnostic_item(cx, obj_ty, sym::FsOpenOptions) && !arguments.is_empty() { + if !arguments.is_empty() && is_open_options(cx, obj_ty) { let argument_option = match arguments[0].kind { ExprKind::Lit(span) => { if let Spanned { @@ -48,11 +76,12 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec .. } = span { - if *lit { Argument::True } else { Argument::False } + Argument::Set(*lit) } else { // The function is called with a literal which is not a boolean literal. // This is theoretically possible, but not very likely. - return; + // We'll ignore it for now + return get_open_options(cx, receiver, options); } }, _ => Argument::Unknown, @@ -60,106 +89,77 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec match path.ident.as_str() { "create" => { - options.push((OpenOption::Create, argument_option)); + options.push((OpenOption::Create, argument_option, span)); + }, + "create_new" => { + options.push((OpenOption::CreateNew, argument_option, span)); }, "append" => { - options.push((OpenOption::Append, argument_option)); + options.push((OpenOption::Append, argument_option, span)); }, "truncate" => { - options.push((OpenOption::Truncate, argument_option)); + options.push((OpenOption::Truncate, argument_option, span)); }, "read" => { - options.push((OpenOption::Read, argument_option)); + options.push((OpenOption::Read, argument_option, span)); }, "write" => { - options.push((OpenOption::Write, argument_option)); + options.push((OpenOption::Write, argument_option, span)); + }, + _ => { + // Avoid linting altogether if this method is from a trait. + // This might be a user defined extension trait with a method like `truncate_write` + // which would be a false positive + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(argument.hir_id) + && cx.tcx.trait_of_item(method_def_id).is_some() + { + return false; + } }, - _ => (), } - get_open_options(cx, receiver, options); + get_open_options(cx, receiver, options) + } else { + false } + } else if let ExprKind::Call(callee, _) = argument.kind + && let ExprKind::Path(path) = callee.kind + && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() + { + match_any_def_paths( + cx, + did, + &[ + &paths::TOKIO_IO_OPEN_OPTIONS_NEW, + &paths::OPEN_OPTIONS_NEW, + &paths::FILE_OPTIONS, + &paths::TOKIO_FILE_OPTIONS, + ], + ) + .is_some() + } else { + false } } -fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) { - let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false); - let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) = - (false, false, false, false, false); - // This code is almost duplicated (oh, the irony), but I haven't found a way to - // unify it. - - for option in options { - match *option { - (OpenOption::Create, arg) => { - if create { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `create` is called more than once", - ); - } else { - create = true; - } - create_arg = create_arg || (arg == Argument::True); - }, - (OpenOption::Append, arg) => { - if append { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `append` is called more than once", - ); - } else { - append = true; - } - append_arg = append_arg || (arg == Argument::True); - }, - (OpenOption::Truncate, arg) => { - if truncate { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `truncate` is called more than once", - ); - } else { - truncate = true; - } - truncate_arg = truncate_arg || (arg == Argument::True); - }, - (OpenOption::Read, arg) => { - if read { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `read` is called more than once", - ); - } else { - read = true; - } - read_arg = read_arg || (arg == Argument::True); - }, - (OpenOption::Write, arg) => { - if write { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `write` is called more than once", - ); - } else { - write = true; - } - write_arg = write_arg || (arg == Argument::True); - }, +fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument, Span)], span: Span) { + // The args passed to these methods, if they have been called + let mut options = FxHashMap::default(); + for (option, arg, sp) in settings { + if let Some((_, prev_span)) = options.insert(option.clone(), (arg.clone(), *sp)) { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + prev_span, + &format!("the method `{}` is called more than once", &option), + ); } } - if read && truncate && read_arg && truncate_arg && !(write && write_arg) { + if let Some((Argument::Set(true), _)) = options.get(&OpenOption::Read) + && let Some((Argument::Set(true), _)) = options.get(&OpenOption::Truncate) + && let None | Some((Argument::Set(false), _)) = options.get(&OpenOption::Write) + { span_lint( cx, NONSENSICAL_OPEN_OPTIONS, @@ -167,7 +167,10 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "file opened with `truncate` and `read`", ); } - if append && truncate && append_arg && truncate_arg { + + if let Some((Argument::Set(true), _)) = options.get(&OpenOption::Append) + && let Some((Argument::Set(true), _)) = options.get(&OpenOption::Truncate) + { span_lint( cx, NONSENSICAL_OPEN_OPTIONS, @@ -175,4 +178,29 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "file opened with `append` and `truncate`", ); } + + if let Some((Argument::Set(true), create_span)) = options.get(&OpenOption::Create) + && let None = options.get(&OpenOption::Truncate) + && let None | Some((Argument::Set(false), _)) = options.get(&OpenOption::Append) + { + span_lint_and_then( + cx, + SUSPICIOUS_OPEN_OPTIONS, + *create_span, + "file opened with `create`, but `truncate` behavior not defined", + |diag| { + diag.span_suggestion( + create_span.shrink_to_hi(), + "add", + ".truncate(true)".to_string(), + rustc_errors::Applicability::MaybeIncorrect, + ) + .help("if you intend to overwrite an existing file entirely, call `.truncate(true)`") + .help( + "if you instead know that you may want to keep some parts of the old file, call `.truncate(false)`", + ) + .help("alternatively, use `.append(true)` to append to the file instead of overwriting it"); + }, + ); + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 756dbe62d84d6..88e2af15658ff 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -97,7 +97,7 @@ pub(super) fn check( }; let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, "..")); - let suggestion = format!("try using {method_hint} instead"); + let suggestion = format!("consider using {method_hint}"); let msg = format!("called `{current_method}` on an `Option` value"); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_err_ok.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_err_ok.rs index 91e39d5a1cd27..4e424d4c066a6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_err_ok.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_err_ok.rs @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( OPTION_MAP_OR_ERR_OK, expr.span, msg, - "try using `ok_or` instead", + "consider using `ok_or`", format!("{self_snippet}.ok_or({err_snippet})"), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs index ff4d8cc9e3e1a..193deafccf650 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs @@ -72,7 +72,7 @@ pub(super) fn check<'tcx>( OPTION_MAP_OR_NONE, expr.span, msg, - "try using `map` instead", + "consider using `map`", format!("{self_snippet}.map({arg_snippet} {func_snippet})"), Applicability::MachineApplicable, ); @@ -85,7 +85,7 @@ pub(super) fn check<'tcx>( OPTION_MAP_OR_NONE, expr.span, msg, - "try using `and_then` instead", + "consider using `and_then`", format!("{self_snippet}.and_then({func_snippet})"), Applicability::MachineApplicable, ); @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( RESULT_MAP_OR_INTO_OPTION, expr.span, msg, - "try using `ok` instead", + "consider using `ok`", format!("{self_snippet}.ok()"), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs b/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs index bc16a11281620..3b0dc506305b5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( RESULT_MAP_OR_INTO_OPTION, expr.span, msg, - "try using `ok` instead", + "consider using `ok`", format!("{self_snippet}.ok()"), Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index 6339011c92f58..ef1baa6c98820 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -63,7 +63,7 @@ pub(super) fn check<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), &msg, - "use `any()` instead", + "consider using", format!( "any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( SEARCH_IS_SOME, expr.span, &msg, - "use `!_.any()` instead", + "consider using", format!( "!{iter}.any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) @@ -118,7 +118,7 @@ pub(super) fn check<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), &msg, - "use `contains()` instead", + "consider using", format!("contains({find_arg})"), applicability, ); @@ -132,7 +132,7 @@ pub(super) fn check<'tcx>( SEARCH_IS_SOME, expr.span, &msg, - "use `!_.contains()` instead", + "consider using", format!("!{string}.contains({find_arg})"), applicability, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs index 3983f0c0cabd4..363b1f2b81229 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs @@ -57,7 +57,7 @@ pub(super) fn check( SINGLE_CHAR_PATTERN, arg.span, "single-character string constant used as pattern", - "try using a `char` instead", + "consider using a `char`", hint, applicability, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs index e2b389e96dae0..c3ad4db387592 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( UNNECESSARY_JOIN, span.with_hi(expr.span.hi()), r#"called `.collect::>().join("")` on an iterator"#, - "try using", + "consider using", "collect::()".to_owned(), applicability, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index 696e5e74d60a5..6911da69b945e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -203,7 +203,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_SORT_BY, expr.span, - "use Vec::sort_by_key here instead", + "consider using `sort_by_key`", "try", format!( "{}.sort{}_by_key(|{}| {})", @@ -226,7 +226,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_SORT_BY, expr.span, - "use Vec::sort here instead", + "consider using `sort`", "try", format!( "{}.sort{}()", diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index 66727e5a29dc6..514015af0455c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; -use clippy_utils::{get_parent_expr, is_diag_trait_item, match_def_path, paths, peel_blocks}; +use clippy_utils::{ + get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, paths, peel_blocks, strip_pat_refs, +}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -108,9 +110,12 @@ fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { match arg.kind { - hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + hir::ExprKind::Closure(&hir::Closure { body, .. }) // If it's a closure, we need to check what is called. - let closure_body = cx.tcx.hir().body(body); + if let closure_body = cx.tcx.hir().body(body) + && let [param] = closure_body.params + && let hir::PatKind::Binding(_, local_id, ..) = strip_pat_refs(param.pat).kind => + { let closure_expr = peel_blocks(closure_body.value); match closure_expr.kind { hir::ExprKind::MethodCall(method, obj, [], _) => { @@ -122,14 +127,17 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + && path_to_local_id(obj, local_id) { true } else { false } }, - hir::ExprKind::Call(call, [_]) => { - if let hir::ExprKind::Path(qpath) = call.kind { + hir::ExprKind::Call(call, [recv]) => { + if let hir::ExprKind::Path(qpath) = call.kind + && path_to_local_id(recv, local_id) + { check_qpath(cx, qpath, call.hir_id) } else { false 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 64ef709e2fa2d..d2eef6ae4338e 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 @@ -5,16 +5,15 @@ use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{ BlockCheckMode, Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, - PatKind, QPath, + PatKind, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::associated_body; -use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; use rustc_session::impl_lint_pass; @@ -234,12 +233,29 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { } } - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - cx.tcx.hir().visit_all_item_likes_in_crate(&mut FnNeedsMutVisitor { - cx, - used_fn_def_ids: &mut self.used_fn_def_ids, - }); + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + // #11182; do not lint if mutability is required elsewhere + if let ExprKind::Path(..) = expr.kind + && let Some(parent) = get_parent_node(cx.tcx, expr.hir_id) + && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(expr).kind() + && let Some(def_id) = def_id.as_local() + { + if let Node::Expr(e) = parent + && let ExprKind::Call(call, _) = e.kind + && call.hir_id == expr.hir_id + { + return; + } + // We don't need to check each argument individually as you cannot coerce a function + // taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's + // passed as a `fn`-like argument (or is unified) and should ignore every "unused" + // argument entirely + self.used_fn_def_ids.insert(def_id); + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { for (fn_def_id, unused) in self .fn_def_ids_to_maybe_unused_mut .iter() @@ -501,48 +517,3 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { } } } - -/// A final pass to check for paths referencing this function that require the argument to be -/// `&mut`, basically if the function is ever used as a `fn`-like argument. -struct FnNeedsMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - used_fn_def_ids: &'a mut FxHashSet, -} - -impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> { - type NestedFilter = OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, hir_id: HirId, _: Span) { - walk_qpath(self, qpath, hir_id); - - let Self { cx, used_fn_def_ids } = self; - - // #11182; do not lint if mutability is required elsewhere - if let Node::Expr(expr) = cx.tcx.hir_node(hir_id) - && let Some(parent) = get_parent_node(cx.tcx, expr.hir_id) - && let ty::FnDef(def_id, _) = cx - .tcx - .typeck(cx.tcx.hir().enclosing_body_owner(hir_id)) - .expr_ty(expr) - .kind() - && let Some(def_id) = def_id.as_local() - { - if let Node::Expr(e) = parent - && let ExprKind::Call(call, _) = e.kind - && call.hir_id == expr.hir_id - { - return; - } - - // We don't need to check each argument individually as you cannot coerce a function - // taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's - // passed as a `fn`-like argument (or is unified) and should ignore every "unused" - // argument entirely - used_fn_def_ids.insert(def_id); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 6bbe427ea2988..0d234f7f9b52c 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,16 +1,18 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; -use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks}; +use clippy_utils::{any_parent_is_automatically_derived, get_parent_node, is_lint_allowed, path_to_local, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, ItemKind, Node, PatKind, Stmt, StmtKind, UnsafeSource, + is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, Node, PatKind, Stmt, + StmtKind, UnsafeSource, }; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; +use rustc_span::Span; use std::ops::Deref; declare_clippy_lint! { @@ -74,94 +76,125 @@ declare_clippy_lint! { "outer expressions with no effect" } -declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]); +#[derive(Default)] +pub struct NoEffect { + underscore_bindings: HirIdMap, + local_bindings: Vec>, +} + +impl_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]); impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if check_no_effect(cx, stmt) { + if self.check_no_effect(cx, stmt) { return; } check_unnecessary_operation(cx, stmt); } -} -fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { - if let StmtKind::Semi(expr) = stmt.kind { - // move `expr.span.from_expansion()` ahead - if expr.span.from_expansion() { - return false; + fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) { + self.local_bindings.push(Vec::default()); + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) { + for hir_id in self.local_bindings.pop().unwrap() { + if let Some(span) = self.underscore_bindings.remove(&hir_id) { + span_lint_hir( + cx, + NO_EFFECT_UNDERSCORE_BINDING, + hir_id, + span, + "binding to `_` prefixed variable with no side-effect", + ); + } } - let expr = peel_blocks(expr); + } - if is_operator_overridden(cx, expr) { - // Return `true`, to prevent `check_unnecessary_operation` from - // linting on this statement as well. - return true; + fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if let Some(def_id) = path_to_local(expr) { + self.underscore_bindings.remove(&def_id); } - if has_no_effect(cx, expr) { - span_lint_hir_and_then( - cx, - NO_EFFECT, - expr.hir_id, - stmt.span, - "statement with no effect", - |diag| { - for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { - if let Node::Item(item) = parent.1 - && let ItemKind::Fn(..) = item.kind - && let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) - && let [.., final_stmt] = block.stmts - && final_stmt.hir_id == stmt.hir_id - { - let expr_ty = cx.typeck_results().expr_ty(expr); - let mut ret_ty = cx - .tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .output() - .skip_binder(); + } +} + +impl NoEffect { + fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { + if let StmtKind::Semi(expr) = stmt.kind { + // move `expr.span.from_expansion()` ahead + if expr.span.from_expansion() { + return false; + } + let expr = peel_blocks(expr); - // Remove `impl Future` to get `T` - if cx.tcx.ty_is_opaque_future(ret_ty) - && let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty) + if is_operator_overridden(cx, expr) { + // Return `true`, to prevent `check_unnecessary_operation` from + // linting on this statement as well. + return true; + } + if has_no_effect(cx, expr) { + span_lint_hir_and_then( + cx, + NO_EFFECT, + expr.hir_id, + stmt.span, + "statement with no effect", + |diag| { + for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { + if let Node::Item(item) = parent.1 + && let ItemKind::Fn(..) = item.kind + && let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) + && let [.., final_stmt] = block.stmts + && final_stmt.hir_id == stmt.hir_id { - ret_ty = true_ret_ty; - } + let expr_ty = cx.typeck_results().expr_ty(expr); + let mut ret_ty = cx + .tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .output() + .skip_binder(); + + // Remove `impl Future` to get `T` + if cx.tcx.ty_is_opaque_future(ret_ty) + && let Some(true_ret_ty) = + cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty) + { + ret_ty = true_ret_ty; + } - if !ret_ty.is_unit() && ret_ty == expr_ty { - diag.span_suggestion( - stmt.span.shrink_to_lo(), - "did you mean to return it?", - "return ", - Applicability::MaybeIncorrect, - ); + if !ret_ty.is_unit() && ret_ty == expr_ty { + diag.span_suggestion( + stmt.span.shrink_to_lo(), + "did you mean to return it?", + "return ", + Applicability::MaybeIncorrect, + ); + } } } - } - }, - ); - return true; - } - } else if let StmtKind::Local(local) = stmt.kind { - if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) - && let Some(init) = local.init - && local.els.is_none() - && !local.pat.span.from_expansion() - && has_no_effect(cx, init) - && let PatKind::Binding(_, _, ident, _) = local.pat.kind - && ident.name.to_ident_string().starts_with('_') - { - span_lint_hir( - cx, - NO_EFFECT_UNDERSCORE_BINDING, - init.hir_id, - stmt.span, - "binding to `_` prefixed variable with no side-effect", - ); - return true; + }, + ); + return true; + } + } else if let StmtKind::Local(local) = stmt.kind { + if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) + && let Some(init) = local.init + && local.els.is_none() + && !local.pat.span.from_expansion() + && has_no_effect(cx, init) + && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind + && ident.name.to_ident_string().starts_with('_') + && !any_parent_is_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 } - false } fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs index 9db2e24630aac..a69989e400be4 100644 --- a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs +++ b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; +use clippy_utils::std_or_core; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use super::PTR_EQ; -static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; - pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -26,13 +25,14 @@ pub(super) fn check<'tcx>( && let Some(left_snip) = snippet_opt(cx, left_var.span) && let Some(right_snip) = snippet_opt(cx, right_var.span) { + let Some(top_crate) = std_or_core(cx) else { return }; span_lint_and_sugg( cx, PTR_EQ, expr.span, - LINT_MSG, + &format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), "try", - format!("std::ptr::eq({left_snip}, {right_snip})"), + format!("{top_crate}::ptr::eq({left_snip}, {right_snip})"), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs index 00465ce43813e..88b5a6cfe2aa7 100644 --- a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs +++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { }; let is_visible = |field: &FieldDef<'_>| match self.behavior { - PubUnderscoreFieldsBehaviour::PublicallyExported => cx.effective_visibilities.is_reachable(field.def_id), + PubUnderscoreFieldsBehaviour::PubliclyExported => cx.effective_visibilities.is_reachable(field.def_id), PubUnderscoreFieldsBehaviour::AllPubFields => { // If there is a visibility span then the field is marked pub in some way. !field.vis_span.is_empty() diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs index 62f3c09aa7e26..650324d4249eb 100644 --- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs +++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::get_enclosing_block; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr; -use core::ops::ControlFlow; -use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; + +use hir::{Expr, ExprKind, HirId, Local, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -49,57 +51,40 @@ declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]); impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) { - for (idx, stmt) in block.stmts.iter().enumerate() { - if !stmt.span.from_expansion() - // matches `let v = Vec::new();` - && let StmtKind::Local(local) = stmt.kind - && let Local { pat, init: Some(init), .. } = local - && let PatKind::Binding(_, _, ident, _) = pat.kind + for stmt in block.stmts { + if stmt.span.from_expansion() { + return; + } + + if let StmtKind::Local(local) = stmt.kind + && let Local { + pat, init: Some(init), .. + } = local + && let PatKind::Binding(_, id, ident, _) = pat.kind && let Some(vec_init_kind) = get_vec_init_kind(cx, init) { - let visitor = |expr: &Expr<'_>| { - if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind - && let PathSegment { - ident: read_or_read_exact, - .. - } = *path - && matches!(read_or_read_exact.as_str(), "read" | "read_exact") - && let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind - && let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind - && let [inner_seg] = inner_path.segments - && ident.name == inner_seg.ident.name - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } + let mut visitor = ReadVecVisitor { + local_id: id, + read_zero_expr: None, + has_resize: false, }; - let (read_found, next_stmt_span) = if let Some(next_stmt) = block.stmts.get(idx + 1) { - // case { .. stmt; stmt; .. } - (for_each_expr(next_stmt, visitor).is_some(), next_stmt.span) - } else if let Some(e) = block.expr { - // case { .. stmt; expr } - (for_each_expr(e, visitor).is_some(), e.span) - } else { + let Some(enclosing_block) = get_enclosing_block(cx, id) else { return; }; + visitor.visit_block(enclosing_block); - if read_found && !next_stmt_span.from_expansion() { + if let Some(expr) = visitor.read_zero_expr { let applicability = Applicability::MaybeIncorrect; match vec_init_kind { VecInitKind::WithConstCapacity(len) => { span_lint_and_sugg( cx, READ_ZERO_BYTE_VEC, - next_stmt_span, + expr.span, "reading zero byte data to `Vec`", "try", - format!( - "{}.resize({len}, 0); {}", - ident.as_str(), - snippet(cx, next_stmt_span, "..") - ), + format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")), applicability, ); }, @@ -108,25 +93,20 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { span_lint_and_sugg( cx, READ_ZERO_BYTE_VEC, - next_stmt_span, + expr.span, "reading zero byte data to `Vec`", "try", format!( "{}.resize({}, 0); {}", ident.as_str(), snippet(cx, e.span, ".."), - snippet(cx, next_stmt_span, "..") + snippet(cx, expr.span, "..") ), applicability, ); }, _ => { - span_lint( - cx, - READ_ZERO_BYTE_VEC, - next_stmt_span, - "reading zero byte data to `Vec`", - ); + span_lint(cx, READ_ZERO_BYTE_VEC, expr.span, "reading zero byte data to `Vec`"); }, } } @@ -134,3 +114,47 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { } } } + +struct ReadVecVisitor<'tcx> { + local_id: HirId, + read_zero_expr: Option<&'tcx Expr<'tcx>>, + has_resize: bool, +} + +impl<'tcx> Visitor<'tcx> for ReadVecVisitor<'tcx> { + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(path, receiver, args, _) = e.kind { + let PathSegment { ident, .. } = *path; + + match ident.as_str() { + "read" | "read_exact" => { + let [arg] = args else { return }; + if let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind + && let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind + && let [inner_seg] = inner_path.segments + && let Res::Local(res_id) = inner_seg.res + && self.local_id == res_id + { + self.read_zero_expr = Some(e); + return; + } + }, + "resize" => { + // If the Vec is resized, then it's a valid read + if let ExprKind::Path(QPath::Resolved(_, inner_path)) = receiver.kind + && let Res::Local(res_id) = inner_path.res + && self.local_id == res_id + { + self.has_resize = true; + return; + } + }, + _ => {}, + } + } + + if !self.has_resize && self.read_zero_expr.is_none() { + walk_expr(self, e); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs index 2cd3e57f885a2..6540626f7d5a3 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::{ExpnKind, MacroKind, Span}; declare_clippy_lint! { /// ### What it does @@ -39,6 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if !block.span.from_expansion() && let Some(expr) = block.expr + && !from_attr_macro(expr.span) && let t_expr = cx.typeck_results().expr_ty(expr) && t_expr.is_unit() && let mut app = Applicability::MachineApplicable @@ -63,3 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { } } } + +fn from_attr_macro(span: Span) -> bool { + matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Attr, _)) +} diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs index 8e181c3ccc743..0387742077447 100644 --- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs +++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::{is_from_proc_macro, is_in_test_function}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; @@ -88,16 +88,18 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn { }; cx.tcx.hir().visit_all_item_likes_in_crate(&mut v); - for usage in self.def_id_to_usage.values() { + for (&def_id, usage) in &self.def_id_to_usage { let single_call_fn_span = usage.0; if let [caller_span] = *usage.1 { - span_lint_and_help( + span_lint_hir_and_then( cx, SINGLE_CALL_FN, + cx.tcx.local_def_id_to_hir_id(def_id), single_call_fn_span, "this function is only used once", - Some(caller_span), - "used here", + |diag| { + diag.span_help(caller_span, "used here"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index e4054393d0ad7..768623b5d0347 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -390,6 +390,14 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &' } } +fn get_ty_res(ty: Ty<'_>) -> Option { + match ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => Some(path.res), + TyKind::Path(QPath::TypeRelative(ty, _)) => get_ty_res(*ty), + _ => None, + } +} + // FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { ComparableTraitRef( @@ -401,10 +409,8 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { .filter_map(|segment| { // get trait bound type arguments Some(segment.args?.args.iter().filter_map(|arg| { - if let GenericArg::Type(ty) = arg - && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind - { - return Some(path.res); + if let GenericArg::Type(ty) = arg { + return get_ty_res(**ty); } None })) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs index 7d31c375f8cf2..2a6c248125459 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_INT_TO_CHAR; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::{std_or_core, sugg}; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -25,6 +25,7 @@ pub(super) fn check<'tcx>( e.span, &format!("transmute from a `{from_ty}` to a `char`"), |diag| { + let Some(top_crate) = std_or_core(cx) else { return }; let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(_) = from_ty.kind() { arg.as_ty(ast::UintTy::U32.name_str()) @@ -34,7 +35,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("std::char::from_u32({arg}).unwrap()"), + format!("{top_crate}::char::from_u32({arg}).unwrap()"), Applicability::Unspecified, ); }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 98e9ea2d7751f..6c885ebdea11d 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -1,7 +1,7 @@ use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; -use clippy_utils::sugg; +use clippy_utils::{std_or_core, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -25,6 +25,8 @@ pub(super) fn check<'tcx>( && let ty::Uint(ty::UintTy::U8) = slice_ty.kind() && from_mutbl == to_mutbl { + let Some(top_crate) = std_or_core(cx) else { return true }; + let postfix = if *from_mutbl == Mutability::Mut { "_mut" } else { "" }; let snippet = snippet(cx, arg.span, ".."); @@ -36,9 +38,9 @@ pub(super) fn check<'tcx>( &format!("transmute from a `{from_ty}` to a `{to_ty}`"), "consider using", if const_context { - format!("std::str::from_utf8_unchecked{postfix}({snippet})") + format!("{top_crate}::str::from_utf8_unchecked{postfix}({snippet})") } else { - format!("std::str::from_utf8{postfix}({snippet}).unwrap()") + format!("{top_crate}::str::from_utf8{postfix}({snippet}).unwrap()") }, Applicability::MaybeIncorrect, ); diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index b418db53ea47c..209035804e43e 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -167,7 +167,15 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca false } }, - ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => { + ExprKind::MethodCall(segment, receiver, &[_arg], _) if segment.ident.name == name.name => { + if let Some(ty) = cx.typeck_results().expr_ty_opt(receiver) + && let Some(ty_id) = get_ty_def_id(ty) + && self_arg != ty_id + { + // Since this called on a different type, the lint should not be + // triggered here. + return; + } if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) && trait_id == trait_def_id 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 1de9adfcb963a..adc66e15ff501 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,9 +1,10 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{is_trait_method, is_try, match_trait_method, paths}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths}; +use hir::{ExprKind, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -45,126 +46,219 @@ declare_clippy_lint! { declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); +#[derive(Copy, Clone)] +enum IoOp { + AsyncWrite(bool), + AsyncRead(bool), + SyncRead(bool), + SyncWrite(bool), +} + impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { - fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else { - return; - }; + /// We perform the check on the block level. + /// If we want to catch match and if expressions that act as returns of the block + /// we need to check them at `check_expr` or `check_block` as they are not stmts + /// but we can't check them at `check_expr` because we need the broader context + /// because we should do this only for the final expression of the block, and not for + /// `StmtKind::Local` which binds values => the io amount is used. + /// + /// To check for unused io amount in stmts, we only consider `StmtKind::Semi`. + /// `StmtKind::Local` is not considered because it binds values => the io amount is used. + /// `StmtKind::Expr` is not considered because requires unit type => the io amount is used. + /// `StmtKind::Item` is not considered because it's not an expression. + /// + /// We then check the individual expressions via `check_expr`. We use the same logic for + /// semi expressions and the final expression as we need to check match and if expressions + /// for binding of the io amount to `Ok(_)`. + /// + /// We explicitly check for the match source to be Normal as it needs special logic + /// to consider the arms, and we want to avoid breaking the logic for situations where things + /// get desugared to match. + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) { + for stmt in block.stmts { + if let hir::StmtKind::Semi(exp) = stmt.kind { + check_expr(cx, exp); + } + } - match expr.kind { - hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => { - if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind { - if matches!( - func.kind, - hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) - ) { - check_map_error(cx, arg_0, expr); + if let Some(exp) = block.expr + && matches!(exp.kind, hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, _)) + { + check_expr(cx, exp); + } + } +} + +fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { + match expr.kind { + hir::ExprKind::If(cond, _, _) + if let ExprKind::Let(hir::Let { pat, init, .. }) = cond.kind + && pattern_is_ignored_ok(cx, pat) + && let Some(op) = should_lint(cx, init) => + { + emit_lint(cx, cond.span, op, &[pat.span]); + }, + hir::ExprKind::Match(expr, arms, hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => { + let found_arms: Vec<_> = arms + .iter() + .filter_map(|arm| { + if pattern_is_ignored_ok(cx, arm.pat) { + Some(arm.span) + } else { + None } - } else { - check_map_error(cx, res, expr); - } - }, - hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() { - "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => { - check_map_error(cx, arg_0, expr); - }, - _ => (), - }, - _ => (), + }) + .collect(); + if !found_arms.is_empty() { + emit_lint(cx, expr.span, op, found_arms.as_slice()); + } + }, + _ if let Some(op) = should_lint(cx, expr) => { + emit_lint(cx, expr.span, op, &[]); + }, + _ => {}, + }; +} + +fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option { + inner = unpack_match(inner); + inner = unpack_try(inner); + inner = unpack_call_chain(inner); + inner = unpack_await(inner); + // we type-check it to get whether it's a read/write or their vectorized forms + // and keep only the ones that are produce io amount + check_io_mode(cx, inner) +} + +fn pattern_is_ignored_ok(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> bool { + // the if checks whether we are in a result Ok( ) pattern + // and the return checks whether it is unhandled + + if let PatKind::TupleStruct(ref path, inner_pat, ddp) = pat.kind + // we check against Result::Ok to avoid linting on Err(_) or something else. + && is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk) + { + return match (inner_pat, ddp.as_opt_usize()) { + // Ok(_) pattern + ([inner_pat], None) if matches!(inner_pat.kind, PatKind::Wild) => true, + // Ok(..) pattern + ([], Some(0)) => true, + _ => false, + }; + } + false +} + +fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { + while let hir::ExprKind::MethodCall(path, receiver, ..) = expr.kind { + if matches!( + path.ident.as_str(), + "unwrap" | "expect" | "unwrap_or" | "unwrap_or_else" | "ok" | "is_ok" | "is_err" | "or_else" | "or" + ) { + expr = receiver; + } else { + break; } } + expr +} + +fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { + while let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind + && matches!( + func.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) + ) + { + expr = arg_0; + } + expr +} + +fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { + while let hir::ExprKind::Match(res, _, _) = expr.kind { + expr = res; + } + expr } /// If `expr` is an (e).await, return the inner expression "e" that's being /// waited on. Otherwise return None. -fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> { +fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &hir::Expr<'a> { if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind { if matches!( func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) ) { - return Some(arg_0); + return arg_0; } } } - - None + expr } -fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { - let mut call = call; - while let hir::ExprKind::MethodCall(path, receiver, ..) = call.kind { - if matches!(path.ident.as_str(), "or" | "or_else" | "ok") { - call = receiver; - } else { - break; - } - } +/// Check whether the current expr is a function call for an IO operation +fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option { + let hir::ExprKind::MethodCall(path, ..) = call.kind else { + return None; + }; - if let Some(call) = try_remove_await(call) { - check_method_call(cx, call, expr, true); - } else { - check_method_call(cx, call, expr, false); + let vectorized = match path.ident.as_str() { + "write_vectored" | "read_vectored" => true, + "write" | "read" => false, + _ => { + return None; + }, + }; + + match ( + is_trait_method(cx, call, sym::IoRead), + is_trait_method(cx, call, sym::IoWrite), + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT), + match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) + || match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT), + ) { + (true, _, _, _) => Some(IoOp::SyncRead(vectorized)), + (_, true, _, _) => Some(IoOp::SyncWrite(vectorized)), + (_, _, true, _) => Some(IoOp::AsyncRead(vectorized)), + (_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)), + _ => None, } } -fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) { - if let hir::ExprKind::MethodCall(path, ..) = call.kind { - let symbol = path.ident.as_str(); - let read_trait = if is_await { - match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) - || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) - } else { - is_trait_method(cx, call, sym::IoRead) - }; - let write_trait = if is_await { - match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) - || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) - } else { - is_trait_method(cx, call, sym::IoWrite) - }; +fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) { + let (msg, help) = match op { + IoOp::AsyncRead(false) => ( + "read amount is not handled", + Some("use `AsyncReadExt::read_exact` instead, or handle partial reads"), + ), + IoOp::SyncRead(false) => ( + "read amount is not handled", + Some("use `Read::read_exact` instead, or handle partial reads"), + ), + IoOp::SyncWrite(false) => ( + "written amount is not handled", + Some("use `Write::write_all` instead, or handle partial writes"), + ), + IoOp::AsyncWrite(false) => ( + "written amount is not handled", + Some("use `AsyncWriteExt::write_all` instead, or handle partial writes"), + ), + IoOp::SyncRead(true) | IoOp::AsyncRead(true) => ("read amount is not handled", None), + IoOp::SyncWrite(true) | IoOp::AsyncWrite(true) => ("written amount is not handled", None), + }; - match (read_trait, write_trait, symbol, is_await) { - (true, _, "read", false) => span_lint_and_help( - cx, - UNUSED_IO_AMOUNT, - expr.span, - "read amount is not handled", - None, - "use `Read::read_exact` instead, or handle partial reads", - ), - (true, _, "read", true) => span_lint_and_help( - cx, - UNUSED_IO_AMOUNT, - expr.span, - "read amount is not handled", - None, - "use `AsyncReadExt::read_exact` instead, or handle partial reads", - ), - (true, _, "read_vectored", _) => { - span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"); - }, - (_, true, "write", false) => span_lint_and_help( - cx, - UNUSED_IO_AMOUNT, - expr.span, - "written amount is not handled", - None, - "use `Write::write_all` instead, or handle partial writes", - ), - (_, true, "write", true) => span_lint_and_help( - cx, - UNUSED_IO_AMOUNT, - expr.span, - "written amount is not handled", - None, - "use `AsyncWriteExt::write_all` instead, or handle partial writes", - ), - (_, true, "write_vectored", _) => { - span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"); - }, - _ => (), + span_lint_and_then(cx, UNUSED_IO_AMOUNT, span, msg, |diag| { + if let Some(help_str) = help { + diag.help(help_str); } - } + for span in wild_cards { + diag.span_note( + *span, + "the result is consumed here, but the amount of I/O bytes remains unhandled", + ); + } + }); } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs index 5ddedb24b15a4..4822970e47ef5 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation { ident.span, "non-standard lint formulation", None, - &format!("try using `{}` instead", formulation.correction), + &format!("consider using `{}`", formulation.correction), ); } return; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 4fb615e1d579e..c62ae8d718dba 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -71,7 +71,9 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedType::Str, ] .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied()); + .flat_map(|&ty| cx.tcx.incoherent_impls(ty).into_iter()) + .flatten() + .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) { diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index ad8619f0d3d96..2d0c2cf125364 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,5 +1,6 @@ use rustc_ast::{ast, attr}; use rustc_errors::Applicability; +use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; use rustc_span::sym; use std::str::FromStr; @@ -159,3 +160,14 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } + +pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { + adt.is_variant_list_non_exhaustive() + || tcx.has_attr(adt.did(), sym::non_exhaustive) + || adt.variants().iter().any(|variant_def| { + variant_def.is_field_list_non_exhaustive() || tcx.has_attr(variant_def.def_id, sym::non_exhaustive) + }) + || adt + .all_fields() + .any(|field_def| tcx.has_attr(field_def.did, sym::non_exhaustive)) +} diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 979b117db256a..4fa93ad23c369 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -134,7 +134,7 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two blocks are the same. #[expect(clippy::similar_names)] fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { - use TokenKind::{BlockComment, LineComment, Semi, Whitespace}; + use TokenKind::{Semi, Whitespace}; if left.stmts.len() != right.stmts.len() { return false; } @@ -177,7 +177,7 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } if !eq_span_tokens(self.inner.cx, lstart..lstmt_span.lo, rstart..rstmt_span.lo, |t| { - !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) + !matches!(t, Whitespace | Semi) }) { return false; } @@ -212,7 +212,7 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } eq_span_tokens(self.inner.cx, lstart..lend, rstart..rend, |t| { - !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) + !matches!(t, Whitespace | Semi) }) } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index ebe520b27eb56..4e499ff4cc612 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -536,7 +536,12 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator SimplifiedType::Float(FloatTy::F32), "f64" => SimplifiedType::Float(FloatTy::F64), #[allow(trivial_casts)] - _ => return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]).into_iter().flatten().copied(), + _ => { + return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]) + .into_iter() + .flatten() + .copied(); + }, }; tcx.incoherent_impls(ty).into_iter().flatten().copied() @@ -1712,7 +1717,6 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)), PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), - PatKind::Lit(..) | PatKind::Range(..) => true, PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), PatKind::Or(pats) => { // TODO: should be the honest check, that pats is exhaustive set @@ -1736,7 +1740,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Err(_) => true, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) => true, } } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 0a820a1754cae..0051f78451467 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -26,6 +26,7 @@ pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; +pub const FILE_OPTIONS: [&str; 4] = ["std", "fs", "File", "options"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates @@ -50,6 +51,7 @@ pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"]; +pub const OPEN_OPTIONS_NEW: [&str; 4] = ["std", "fs", "OpenOptions", "new"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; @@ -89,9 +91,15 @@ pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"]; +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"]; +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"]; diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 59ebe685c11ee..11bb16ff6c5bf 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -219,7 +219,8 @@ pub fn implements_trait<'tcx>( /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. /// -/// The `callee_id` argument is used to determine whether this is a function call in a `const fn` environment, used for checking const traits. +/// The `callee_id` argument is used to determine whether this is a function call in a `const fn` +/// environment, used for checking const traits. pub fn implements_trait_with_env<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 2589d46e7da07..d2d56e59ee3fb 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-01-11" +channel = "nightly-2024-01-25" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.stderr new file mode 100644 index 0000000000000..8f0ca76492497 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.stderr @@ -0,0 +1,6 @@ +error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9 + | + = note: `-D clippy::multiple-crate-versions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::multiple_crate_versions)]` + +error: could not compile `multiple-crate-versions` (bin "multiple-crate-versions") due to 1 previous error diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.toml new file mode 100644 index 0000000000000..d39ad6c909a8b --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.toml @@ -0,0 +1,14 @@ +# Should not lint for dev or build dependencies. See issue 5041. + +[package] +# purposefully separated by - instead of _ +name = "multiple-crate-versions" +version = "0.1.0" +publish = false + +[workspace] + +# One of the versions of winapi is only a dev dependency: allowed +[dependencies] +winapi = "0.2" +ansi_term = "=0.11.0" diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/src/main.rs new file mode 100644 index 0000000000000..4bc61dd62992f --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/Cargo.toml new file mode 100644 index 0000000000000..79317659ac072 --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "multiple_crate_versions" +version = "0.1.0" +publish = false + +[workspace] + +[dependencies] +winapi = "0.2" +ansi_term = "=0.11.0" diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/clippy.toml b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/clippy.toml new file mode 100644 index 0000000000000..121361de2fe0b --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/clippy.toml @@ -0,0 +1 @@ +allowed-duplicate-crates = ["winapi"] diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/src/main.rs b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/src/main.rs new file mode 100644 index 0000000000000..4bc61dd62992f --- /dev/null +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.stderr b/src/tools/clippy/tests/ui-internal/check_formulation.stderr index 96fa617601ff4..42a872d9a83f7 100644 --- a/src/tools/clippy/tests/ui-internal/check_formulation.stderr +++ b/src/tools/clippy/tests/ui-internal/check_formulation.stderr @@ -4,7 +4,7 @@ error: non-standard lint formulation LL | /// Check for lint formulations that are correct | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try using `Checks for` instead + = 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)]` @@ -14,7 +14,7 @@ error: non-standard lint formulation LL | /// Detects uses of incorrect formulations | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try using `Checks for` instead + = help: consider using `Checks for` error: aborting due to 2 previous errors 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 03556823a8f50..5ca183d41a93a 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` - --> $DIR/disallow_struct_span_lint.rs:14:5 + --> $DIR/disallow_span_lint.rs:14:5 | LL | cx.span_lint(lint, span, msg, |_| {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,10 +8,10 @@ LL | cx.span_lint(lint, span, msg, |_| {}); = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> $DIR/disallow_struct_span_lint.rs:24:5 + --> $DIR/disallow_span_lint.rs:24:5 | LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-toml/pub_underscore_fields/exported/clippy.toml b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/exported/clippy.toml index 94a0d3554bc7b..9e472bd14ba53 100644 --- a/src/tools/clippy/tests/ui-toml/pub_underscore_fields/exported/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/exported/clippy.toml @@ -1 +1 @@ -pub-underscore-fields-behavior = "PublicallyExported" \ No newline at end of file +pub-underscore-fields-behavior = "PubliclyExported" diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml index b77b4580051ef..2b63f6e5c26f7 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml @@ -3,6 +3,9 @@ foobar = 42 # so is this one barfoo = 53 +# when using underscores instead of dashes, suggest the correct one +allow_mixed_uninlined_format_args = true + # that one is ignored [third-party] clippy-feature = "nightly" diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs index 38009627757c2..49139b60a9f43 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs @@ -1,3 +1,4 @@ +//@no-rustfix //@error-in-other-file: unknown field `foobar`, expected one of fn main() {} 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 ed4fb84df1ae1..fc683e514ba40 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 @@ -11,6 +11,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-private-module-inception allow-unwrap-in-tests allowed-dotfiles + allowed-duplicate-crates allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed @@ -87,6 +88,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-private-module-inception allow-unwrap-in-tests allowed-dotfiles + allowed-duplicate-crates allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed @@ -150,5 +152,82 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect LL | barfoo = 53 | ^^^^^^ -error: aborting due to 2 previous errors +error: error reading Clippy's configuration file: unknown field `allow_mixed_uninlined_format_args`, expected one of + absolute-paths-allowed-crates + absolute-paths-max-segments + accept-comment-above-attributes + accept-comment-above-statement + allow-dbg-in-tests + allow-expect-in-tests + allow-mixed-uninlined-format-args + allow-one-hash-in-raw-strings + allow-print-in-tests + allow-private-module-inception + allow-unwrap-in-tests + allowed-dotfiles + allowed-duplicate-crates + allowed-idents-below-min-chars + allowed-scripts + arithmetic-side-effects-allowed + arithmetic-side-effects-allowed-binary + arithmetic-side-effects-allowed-unary + array-size-threshold + avoid-breaking-exported-api + await-holding-invalid-types + blacklisted-names + cargo-ignore-publish + check-private-items + cognitive-complexity-threshold + cyclomatic-complexity-threshold + disallowed-macros + disallowed-methods + disallowed-names + disallowed-types + doc-valid-idents + enable-raw-pointer-heuristic-for-send + enforce-iter-loop-reborrow + enforced-import-renames + enum-variant-name-threshold + enum-variant-size-threshold + excessive-nesting-threshold + future-size-threshold + ignore-interior-mutability + large-error-threshold + literal-representation-threshold + matches-for-let-else + max-fn-params-bools + max-include-file-size + max-struct-bools + max-suggested-slice-pattern-length + max-trait-bounds + min-ident-chars-threshold + missing-docs-in-crate-items + msrv + pass-by-value-size-limit + pub-underscore-fields-behavior + semicolon-inside-block-ignore-singleline + semicolon-outside-block-ignore-multiline + single-char-binding-names-threshold + stack-size-threshold + standard-macro-braces + struct-field-name-threshold + suppress-restriction-lint-in-const + third-party + too-large-for-stack + too-many-arguments-threshold + too-many-lines-threshold + trivial-copy-size-limit + type-complexity-threshold + unnecessary-box-size + unreadable-literal-lint-fractions + upper-case-acronyms-aggressive + vec-box-size-threshold + verbose-bit-mask-threshold + warn-on-all-wildcard-imports + --> $DIR/$DIR/clippy.toml:7:1 + | +LL | allow_mixed_uninlined_format_args = true + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: perhaps you meant: `allow-mixed-uninlined-format-args` + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs index c58795575160b..ac544737099c6 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs @@ -11,8 +11,8 @@ use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::token::Star; use syn::{ - parse_macro_input, parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, - TraitItem, Type, + parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, + Signature, TraitItem, Type, }; #[proc_macro_attribute] @@ -95,3 +95,33 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea TokenStream::from(quote!(#item)) } + +#[proc_macro_attribute] +pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream { + let mut item = parse_macro_input!(item as ItemFn); + let span = item.block.brace_token.span; + + if item.sig.asyncness.is_some() { + item.sig.asyncness = None; + } + + let crate_name = quote! { fake_crate }; + let block = item.block; + item.block = syn::parse_quote_spanned! { + span => + { + #crate_name::block_on(async { + #block + }) + } + }; + + quote! { + mod #crate_name { + pub fn block_on(_fut: F) {} + } + + #item + } + .into() +} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index 2ab441bbd0c69..efef60567a979 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -85,4 +85,18 @@ fn block_in_match_expr(num: i32) -> i32 { } } +// issue #12162 +macro_rules! timed { + ($name:expr, $body:expr $(,)?) => {{ + let __scope = (); + $body + }}; +} + +fn issue_12162() { + if timed!("check this!", false) { + println!(); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index dd5ae4fb486b7..8bd8a939eb16e 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -85,4 +85,18 @@ fn block_in_match_expr(num: i32) -> i32 { } } +// issue #12162 +macro_rules! timed { + ($name:expr, $body:expr $(,)?) => {{ + let __scope = (); + $body + }}; +} + +fn issue_12162() { + if timed!("check this!", false) { + println!(); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs index 44f8b2eabce3b..9af81f6f7cdda 100644 --- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs @@ -9,17 +9,16 @@ fn simple_examples() { // Simple if true { - //~^ ERROR: all if blocks contain the same code at the start println!("Hello World!"); println!("I'm branch nr: 1"); } else { println!("Hello World!"); println!("I'm branch nr: 2"); } + //~^^^^^^^ ERROR: all if blocks contain the same code at the start // Else if if x == 0 { - //~^ ERROR: all if blocks contain the same code at the start let y = 9; println!("The value y was set to: `{}`", y); let _z = y; @@ -38,6 +37,7 @@ fn simple_examples() { println!("Ha, Pascal allows you to start the array where you want") } + //~^^^^^^^^^^^^^^^^^^^ ERROR: all if blocks contain the same code at the start // Return a value let _ = if x == 7 { @@ -60,7 +60,6 @@ fn simple_but_suggestion_is_invalid() { // Can't be automatically moved because used_value_name is getting used again let used_value_name = 19; if x == 10 { - //~^ ERROR: all if blocks contain the same code at the start let used_value_name = "Different type"; println!("Str: {}", used_value_name); let _ = 1; @@ -69,6 +68,7 @@ fn simple_but_suggestion_is_invalid() { println!("Str: {}", used_value_name); let _ = 2; } + //~^^^^^^^^^ ERROR: all if blocks contain the same code at the start let _ = used_value_name; // This can be automatically moved as `can_be_overridden` is not used again @@ -101,11 +101,11 @@ fn check_if_same_than_else_mask() { } if x == 2019 { - //~^ ERROR: this `if` has identical blocks println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); } else { println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); } + //~^^^^^ ERROR: this `if` has identical blocks } #[allow(clippy::vec_init_then_push)] diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr index 9d4d42fb689e4..317d1577226ca 100644 --- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr +++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -2,7 +2,6 @@ error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:11:5 | LL | / if true { -LL | | LL | | println!("Hello World!"); | |_________________________________^ | @@ -21,7 +20,6 @@ error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:21:5 | LL | / if x == 0 { -LL | | LL | | let y = 9; LL | | println!("The value y was set to: `{}`", y); LL | | let _z = y; @@ -54,7 +52,6 @@ error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:62:5 | LL | / if x == 10 { -LL | | LL | | let used_value_name = "Different type"; LL | | println!("Str: {}", used_value_name); | |_____________________________________________^ @@ -105,13 +102,12 @@ error: this `if` has identical blocks | LL | if x == 2019 { | __________________^ -LL | | LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); LL | | } else { | |_____^ | note: same as this - --> $DIR/shared_at_top.rs:106:12 + --> $DIR/shared_at_top.rs:105:12 | LL | } else { | ____________^ diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs index 2aeacb89c0cb6..b63819d7c3932 100644 --- a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -107,9 +107,9 @@ fn valid_examples() { // Let's test empty blocks if false { - //~^ ERROR: this `if` has identical blocks } else { } + //~^^^ ERROR: this `if` has identical blocks } /// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint @@ -119,7 +119,6 @@ fn trigger_other_lint() { // Same block if x == 0 { - //~^ ERROR: this `if` has identical blocks let u = 19; println!("How are u today?"); let _ = "This is a string"; @@ -128,6 +127,7 @@ fn trigger_other_lint() { println!("How are u today?"); let _ = "This is a string"; } + //~^^^^^^^^^ ERROR: this `if` has identical blocks // Only same expression let _ = if x == 6 { 7 } else { 7 }; @@ -138,28 +138,24 @@ fn trigger_other_lint() { println!("Well I'm the most important block"); "I'm a pretty string" } else if x == 68 { - //~^ ERROR: this `if` has identical blocks println!("I'm a doppelgänger"); - // Don't listen to my clone below if y == 90 { "=^.^=" } else { ":D" } } else { - // Don't listen to my clone above println!("I'm a doppelgänger"); if y == 90 { "=^.^=" } else { ":D" } }; + //~^^^^^^^^^ ERROR: this `if` has identical blocks if x == 0 { println!("I'm single"); } else if x == 68 { - //~^ ERROR: this `if` has identical blocks println!("I'm a doppelgänger"); - // Don't listen to my clone below } else { - // Don't listen to my clone above println!("I'm a doppelgänger"); } + //~^^^^^ ERROR: this `if` has identical blocks } fn main() {} diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr index fcbf12235aa16..0daf2ff6967cd 100644 --- a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr +++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -3,12 +3,11 @@ error: this `if` has identical blocks | LL | if false { | ______________^ -LL | | LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:111:12 + --> $DIR/valid_if_blocks.rs:110:12 | LL | } else { | ____________^ @@ -25,7 +24,6 @@ error: this `if` has identical blocks | LL | if x == 0 { | _______________^ -LL | | LL | | let u = 19; LL | | println!("How are u today?"); LL | | let _ = "This is a string"; @@ -33,7 +31,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:126:12 + --> $DIR/valid_if_blocks.rs:125:12 | LL | } else { | ____________^ @@ -60,20 +58,17 @@ error: this `if` has identical blocks | LL | } else if x == 68 { | _______________________^ -LL | | LL | | println!("I'm a doppelgänger"); -LL | | // Don't listen to my clone below LL | | LL | | if y == 90 { "=^.^=" } else { ":D" } LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:146:12 + --> $DIR/valid_if_blocks.rs:144:12 | LL | } else { | ____________^ -LL | | // Don't listen to my clone above LL | | println!("I'm a doppelgänger"); LL | | LL | | if y == 90 { "=^.^=" } else { ":D" } @@ -81,22 +76,19 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:155:23 + --> $DIR/valid_if_blocks.rs:153:23 | LL | } else if x == 68 { | _______________________^ -LL | | LL | | println!("I'm a doppelgänger"); -LL | | // Don't listen to my clone below LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:159:12 + --> $DIR/valid_if_blocks.rs:155:12 | LL | } else { | ____________^ -LL | | // Don't listen to my clone above LL | | println!("I'm a doppelgänger"); LL | | } | |_____^ diff --git a/src/tools/clippy/tests/ui/crashes/ice-9041.stderr b/src/tools/clippy/tests/ui/crashes/ice-9041.stderr index 00b65f00d787c..67589d1a8ab32 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-9041.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-9041.stderr @@ -2,7 +2,7 @@ error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/ice-9041.rs:5:19 | LL | things.iter().find(|p| is_thing_ready(p)).is_some() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|p| is_thing_ready(&p))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|p| is_thing_ready(&p))` | = note: `-D clippy::search-is-some` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.fixed b/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.fixed new file mode 100644 index 0000000000000..7300bd9bd2abd --- /dev/null +++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.fixed @@ -0,0 +1,28 @@ +#![warn(clippy::default_instead_of_iter_empty)] +#![allow(dead_code)] +#![feature(lang_items)] +#![no_std] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +#[derive(Default)] +struct Iter { + iter: core::iter::Empty, +} + +fn main() { + // Do lint. + let _ = core::iter::empty::(); + let _foo: core::iter::Empty = core::iter::empty(); + + // Do not lint. + let _ = Iter::default(); +} diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.rs b/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.rs new file mode 100644 index 0000000000000..0bc5c7169d129 --- /dev/null +++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.rs @@ -0,0 +1,28 @@ +#![warn(clippy::default_instead_of_iter_empty)] +#![allow(dead_code)] +#![feature(lang_items)] +#![no_std] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +#[derive(Default)] +struct Iter { + iter: core::iter::Empty, +} + +fn main() { + // Do lint. + let _ = core::iter::Empty::::default(); + let _foo: core::iter::Empty = core::iter::Empty::default(); + + // Do not lint. + let _ = Iter::default(); +} diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.stderr b/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.stderr new file mode 100644 index 0000000000000..747a31ecbf366 --- /dev/null +++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty_no_std.stderr @@ -0,0 +1,17 @@ +error: `core::iter::empty()` is the more idiomatic way + --> $DIR/default_instead_of_iter_empty_no_std.rs:23:13 + | +LL | let _ = core::iter::Empty::::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::iter::empty::()` + | + = note: `-D clippy::default-instead-of-iter-empty` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::default_instead_of_iter_empty)]` + +error: `core::iter::empty()` is the more idiomatic way + --> $DIR/default_instead_of_iter_empty_no_std.rs:24:42 + | +LL | let _foo: core::iter::Empty = core::iter::Empty::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::iter::empty()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed index c364c6830578f..e7038082c089c 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed @@ -216,4 +216,44 @@ mod type_already_inferred { } } +mod issue12159 { + #![allow(non_upper_case_globals, clippy::exhaustive_structs)] + pub struct Foo; + + static F: i32 = 1; + impl Foo { + const LIFE_u8: u8 = 42; + const LIFE_i8: i8 = 42; + const LIFE_u16: u16 = 42; + const LIFE_i16: i16 = 42; + const LIFE_u32: u32 = 42; + const LIFE_i32: i32 = 42; + const LIFE_u64: u64 = 42; + const LIFE_i64: i64 = 42; + const LIFE_u128: u128 = 42; + const LIFE_i128: i128 = 42; + const LIFE_usize: usize = 42; + const LIFE_isize: isize = 42; + const LIFE_f32: f32 = 42.; + const LIFE_f64: f64 = 42.; + + const fn consts() { + const LIFE_u8: u8 = 42; + const LIFE_i8: i8 = 42; + const LIFE_u16: u16 = 42; + const LIFE_i16: i16 = 42; + const LIFE_u32: u32 = 42; + const LIFE_i32: i32 = 42; + const LIFE_u64: u64 = 42; + const LIFE_i64: i64 = 42; + const LIFE_u128: u128 = 42; + const LIFE_i128: i128 = 42; + const LIFE_usize: usize = 42; + const LIFE_isize: isize = 42; + const LIFE_f32: f32 = 42.; + const LIFE_f64: f64 = 42.; + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs index ffa7b961d1ced..d8eeda7049194 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs @@ -216,4 +216,44 @@ mod type_already_inferred { } } +mod issue12159 { + #![allow(non_upper_case_globals, clippy::exhaustive_structs)] + pub struct Foo; + + static F: i32 = 1; + impl Foo { + const LIFE_u8: u8 = 42; + const LIFE_i8: i8 = 42; + const LIFE_u16: u16 = 42; + const LIFE_i16: i16 = 42; + const LIFE_u32: u32 = 42; + const LIFE_i32: i32 = 42; + const LIFE_u64: u64 = 42; + const LIFE_i64: i64 = 42; + const LIFE_u128: u128 = 42; + const LIFE_i128: i128 = 42; + const LIFE_usize: usize = 42; + const LIFE_isize: isize = 42; + const LIFE_f32: f32 = 42.; + const LIFE_f64: f64 = 42.; + + const fn consts() { + const LIFE_u8: u8 = 42; + const LIFE_i8: i8 = 42; + const LIFE_u16: u16 = 42; + const LIFE_i16: i16 = 42; + const LIFE_u32: u32 = 42; + const LIFE_i32: i32 = 42; + const LIFE_u64: u64 = 42; + const LIFE_i64: i64 = 42; + const LIFE_u128: u128 = 42; + const LIFE_i128: i128 = 42; + const LIFE_usize: usize = 42; + const LIFE_isize: isize = 42; + const LIFE_f32: f32 = 42.; + const LIFE_f64: f64 = 42.; + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed index a7f5d3ec7cd08..aa7a95405e02f 100644 --- a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed +++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed @@ -121,4 +121,36 @@ pub fn _from_mod() -> _hidden::InPubFn { #[derive(PartialEq)] struct InternalTy; +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct MissingEqNonExhaustive { + foo: u32, + bar: String, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +pub struct MissingEqNonExhaustive1 { + foo: u32, + #[non_exhaustive] + bar: String, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub enum MissingEqNonExhaustive2 { + Foo, + Bar, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +pub enum MissingEqNonExhaustive3 { + Foo, + #[non_exhaustive] + Bar, +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs index 476d2aee23a88..90ac7a7989e00 100644 --- a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs +++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs @@ -121,4 +121,36 @@ pub fn _from_mod() -> _hidden::InPubFn { #[derive(PartialEq)] struct InternalTy; +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct MissingEqNonExhaustive { + foo: u32, + bar: String, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +pub struct MissingEqNonExhaustive1 { + foo: u32, + #[non_exhaustive] + bar: String, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub enum MissingEqNonExhaustive2 { + Foo, + Bar, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +pub enum MissingEqNonExhaustive3 { + Foo, + #[non_exhaustive] + Bar, +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index bff46e55722ba..16f7bafd44b78 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -64,7 +64,7 @@ fn test_units() { /// NaN NaNs /// OAuth GraphQL /// OCaml -/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS +/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry /// WebGL WebGL2 WebGPU /// TensorFlow /// TrueType diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 4e162a97dee9b..4058b2bad74f7 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -64,7 +64,7 @@ fn test_units() { /// NaN NaNs /// OAuth GraphQL /// OCaml -/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS +/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry /// WebGL WebGL2 WebGPU /// TensorFlow /// TrueType diff --git a/src/tools/clippy/tests/ui/from_over_into.fixed b/src/tools/clippy/tests/ui/from_over_into.fixed index 18d285693e603..4a68505ee0b1c 100644 --- a/src/tools/clippy/tests/ui/from_over_into.fixed +++ b/src/tools/clippy/tests/ui/from_over_into.fixed @@ -89,4 +89,12 @@ fn msrv_1_41() { } } +fn issue_12138() { + struct Hello; + + impl From for () { + fn from(val: Hello) {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/from_over_into.rs b/src/tools/clippy/tests/ui/from_over_into.rs index 779ef709e9284..bf3ed0c2b6422 100644 --- a/src/tools/clippy/tests/ui/from_over_into.rs +++ b/src/tools/clippy/tests/ui/from_over_into.rs @@ -89,4 +89,12 @@ fn msrv_1_41() { } } +fn issue_12138() { + struct Hello; + + impl Into<()> for Hello { + fn into(self) {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr index 784843ce5fd55..f1370ed844fa9 100644 --- a/src/tools/clippy/tests/ui/from_over_into.stderr +++ b/src/tools/clippy/tests/ui/from_over_into.stderr @@ -86,5 +86,19 @@ LL ~ fn from(val: Vec) -> Self { LL ~ FromOverInto(val) | -error: aborting due to 6 previous errors +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:95:5 + | +LL | impl Into<()> for Hello { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implementation with `From` + | +LL ~ impl From for () { +LL ~ fn from(val: Hello) {} + | + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/if_same_then_else.rs b/src/tools/clippy/tests/ui/if_same_then_else.rs index e84b20e9fef07..d53e1383d845c 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else.rs +++ b/src/tools/clippy/tests/ui/if_same_then_else.rs @@ -21,7 +21,6 @@ fn foo() -> bool { fn if_same_then_else() { if true { - //~^ ERROR: this `if` has identical blocks Foo { bar: 42 }; 0..10; ..; @@ -38,6 +37,7 @@ fn if_same_then_else() { 0..=10; foo(); } + //~^^^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks if true { Foo { bar: 42 }; @@ -64,19 +64,11 @@ fn if_same_then_else() { foo(); } - let _ = if true { - //~^ ERROR: this `if` has identical blocks - 0.0 - } else { - 0.0 - }; + let _ = if true { 0.0 } else { 0.0 }; + //~^ ERROR: this `if` has identical blocks - let _ = if true { - //~^ ERROR: this `if` has identical blocks - -0.0 - } else { - -0.0 - }; + let _ = if true { -0.0 } else { -0.0 }; + //~^ ERROR: this `if` has identical blocks let _ = if true { 0.0 } else { -0.0 }; @@ -87,15 +79,10 @@ fn if_same_then_else() { foo(); } - let _ = if true { - //~^ ERROR: this `if` has identical blocks - 42 - } else { - 42 - }; + let _ = if true { 42 } else { 42 }; + //~^ ERROR: this `if` has identical blocks if true { - //~^ ERROR: this `if` has identical blocks let bar = if true { 42 } else { 43 }; while foo() { @@ -110,6 +97,7 @@ fn if_same_then_else() { } bar + 1; } + //~^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks if true { let _ = match 42 { diff --git a/src/tools/clippy/tests/ui/if_same_then_else.stderr b/src/tools/clippy/tests/ui/if_same_then_else.stderr index fb33e94e6c3de..281f30f88b46d 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else.stderr +++ b/src/tools/clippy/tests/ui/if_same_then_else.stderr @@ -3,16 +3,16 @@ error: this `if` has identical blocks | LL | if true { | _____________^ -LL | | LL | | Foo { bar: 42 }; LL | | 0..10; +LL | | ..; ... | LL | | foo(); LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:32:12 + --> $DIR/if_same_then_else.rs:31:12 | LL | } else { | ____________^ @@ -29,75 +29,54 @@ LL | | } error: this `if` has identical blocks --> $DIR/if_same_then_else.rs:67:21 | -LL | let _ = if true { - | _____________________^ -LL | | -LL | | 0.0 -LL | | } else { - | |_____^ +LL | let _ = if true { 0.0 } else { 0.0 }; + | ^^^^^^^ | note: same as this - --> $DIR/if_same_then_else.rs:70:12 + --> $DIR/if_same_then_else.rs:67:34 | -LL | } else { - | ____________^ -LL | | 0.0 -LL | | }; - | |_____^ +LL | let _ = if true { 0.0 } else { 0.0 }; + | ^^^^^^^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:74:21 + --> $DIR/if_same_then_else.rs:70:21 | -LL | let _ = if true { - | _____________________^ -LL | | -LL | | -0.0 -LL | | } else { - | |_____^ +LL | let _ = if true { -0.0 } else { -0.0 }; + | ^^^^^^^^ | note: same as this - --> $DIR/if_same_then_else.rs:77:12 + --> $DIR/if_same_then_else.rs:70:35 | -LL | } else { - | ____________^ -LL | | -0.0 -LL | | }; - | |_____^ +LL | let _ = if true { -0.0 } else { -0.0 }; + | ^^^^^^^^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:90:21 + --> $DIR/if_same_then_else.rs:82:21 | -LL | let _ = if true { - | _____________________^ -LL | | -LL | | 42 -LL | | } else { - | |_____^ +LL | let _ = if true { 42 } else { 42 }; + | ^^^^^^ | note: same as this - --> $DIR/if_same_then_else.rs:93:12 + --> $DIR/if_same_then_else.rs:82:33 | -LL | } else { - | ____________^ -LL | | 42 -LL | | }; - | |_____^ +LL | let _ = if true { 42 } else { 42 }; + | ^^^^^^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:97:13 + --> $DIR/if_same_then_else.rs:85:13 | LL | if true { | _____________^ -LL | | LL | | let bar = if true { 42 } else { 43 }; LL | | +LL | | while foo() { ... | LL | | bar + 1; LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:105:12 + --> $DIR/if_same_then_else.rs:92:12 | LL | } else { | ____________^ @@ -110,7 +89,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:250:14 + --> $DIR/if_same_then_else.rs:238:14 | LL | if x { | ______________^ @@ -119,7 +98,7 @@ LL | | } else { | |_________^ | note: same as this - --> $DIR/if_same_then_else.rs:252:16 + --> $DIR/if_same_then_else.rs:240:16 | LL | } else { | ________________^ diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs index 0b171f21d0cc4..e23c77b082744 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.rs +++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs @@ -13,7 +13,6 @@ fn if_same_then_else2() -> Result<&'static str, ()> { if true { - //~^ ERROR: this `if` has identical blocks for _ in &[42] { let foo: &Option<_> = &Some::(42); if foo.is_some() { @@ -32,20 +31,21 @@ fn if_same_then_else2() -> Result<&'static str, ()> { } } } + //~^^^^^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks if true { - //~^ ERROR: this `if` has identical blocks if let Some(a) = Some(42) {} } else { if let Some(a) = Some(42) {} } + //~^^^^^ ERROR: this `if` has identical blocks if true { - //~^ ERROR: this `if` has identical blocks if let (1, .., 3) = (1, 2, 3) {} } else { if let (1, .., 3) = (1, 2, 3) {} } + //~^^^^^ ERROR: this `if` has identical blocks if true { if let (1, .., 3) = (1, 2, 3) {} @@ -90,19 +90,15 @@ fn if_same_then_else2() -> Result<&'static str, ()> { } // Same NaNs - let _ = if true { - //~^ ERROR: this `if` has identical blocks - f32::NAN - } else { - f32::NAN - }; + let _ = if true { f32::NAN } else { f32::NAN }; + //~^ ERROR: this `if` has identical blocks if true { - //~^ ERROR: this `if` has identical blocks Ok("foo")?; } else { Ok("foo")?; } + //~^^^^^ ERROR: this `if` has identical blocks if true { let foo = ""; @@ -122,13 +118,13 @@ fn if_same_then_else2() -> Result<&'static str, ()> { let foo = "bar"; return Ok(&foo[0..]); } else if true { - //~^ ERROR: this `if` has identical blocks let foo = ""; return Ok(&foo[0..]); } else { let foo = ""; return Ok(&foo[0..]); } + //~^^^^^^^ ERROR: this `if` has identical blocks // False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559. if true { diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr index fe68ef2711b59..4e7a7c87dc54b 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.stderr +++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr @@ -3,16 +3,16 @@ error: this `if` has identical blocks | LL | if true { | _____________^ -LL | | LL | | for _ in &[42] { LL | | let foo: &Option<_> = &Some::(42); +LL | | if foo.is_some() { ... | LL | | } LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:25:12 + --> $DIR/if_same_then_else2.rs:24:12 | LL | } else { | ____________^ @@ -31,13 +31,12 @@ error: this `if` has identical blocks | LL | if true { | _____________^ -LL | | LL | | if let Some(a) = Some(42) {} LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:39:12 + --> $DIR/if_same_then_else2.rs:38:12 | LL | } else { | ____________^ @@ -50,13 +49,12 @@ error: this `if` has identical blocks | LL | if true { | _____________^ -LL | | LL | | if let (1, .., 3) = (1, 2, 3) {} LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:46:12 + --> $DIR/if_same_then_else2.rs:45:12 | LL | } else { | ____________^ @@ -67,34 +65,26 @@ LL | | } error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:93:21 | -LL | let _ = if true { - | _____________________^ -LL | | -LL | | f32::NAN -LL | | } else { - | |_____^ +LL | let _ = if true { f32::NAN } else { f32::NAN }; + | ^^^^^^^^^^^^ | note: same as this - --> $DIR/if_same_then_else2.rs:96:12 + --> $DIR/if_same_then_else2.rs:93:39 | -LL | } else { - | ____________^ -LL | | f32::NAN -LL | | }; - | |_____^ +LL | let _ = if true { f32::NAN } else { f32::NAN }; + | ^^^^^^^^^^^^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:100:13 + --> $DIR/if_same_then_else2.rs:96:13 | LL | if true { | _____________^ -LL | | LL | | Ok("foo")?; LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:103:12 + --> $DIR/if_same_then_else2.rs:98:12 | LL | } else { | ____________^ @@ -103,18 +93,17 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:124:20 + --> $DIR/if_same_then_else2.rs:120:20 | LL | } else if true { | ____________________^ -LL | | LL | | let foo = ""; LL | | return Ok(&foo[0..]); LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:128:12 + --> $DIR/if_same_then_else2.rs:123:12 | LL | } else { | ____________^ diff --git a/src/tools/clippy/tests/ui/ineffective_open_options.fixed b/src/tools/clippy/tests/ui/ineffective_open_options.fixed index 3af8f3c5aaa44..88489dc46bbb4 100644 --- a/src/tools/clippy/tests/ui/ineffective_open_options.fixed +++ b/src/tools/clippy/tests/ui/ineffective_open_options.fixed @@ -26,16 +26,23 @@ fn main() { .unwrap(); let file = OpenOptions::new() .create(true) + .truncate(true) .write(true) .append(false) .open("dump.json") .unwrap(); let file = OpenOptions::new() .create(true) + .truncate(true) .write(false) .append(false) .open("dump.json") .unwrap(); let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap(); - let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap(); + let file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open("dump.json") + .unwrap(); } diff --git a/src/tools/clippy/tests/ui/ineffective_open_options.rs b/src/tools/clippy/tests/ui/ineffective_open_options.rs index 4eaf6293c408e..db8bca3e2acdb 100644 --- a/src/tools/clippy/tests/ui/ineffective_open_options.rs +++ b/src/tools/clippy/tests/ui/ineffective_open_options.rs @@ -26,16 +26,23 @@ fn main() { .unwrap(); let file = OpenOptions::new() .create(true) + .truncate(true) .write(true) .append(false) .open("dump.json") .unwrap(); let file = OpenOptions::new() .create(true) + .truncate(true) .write(false) .append(false) .open("dump.json") .unwrap(); let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap(); - let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap(); + let file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open("dump.json") + .unwrap(); } diff --git a/src/tools/clippy/tests/ui/join_absolute_paths.stderr b/src/tools/clippy/tests/ui/join_absolute_paths.stderr index 0c2f89d9978b3..ab4d189ca3a22 100644 --- a/src/tools/clippy/tests/ui/join_absolute_paths.stderr +++ b/src/tools/clippy/tests/ui/join_absolute_paths.stderr @@ -11,7 +11,7 @@ help: if this is unintentional, try removing the starting separator | LL | path.join("sh"); | ~~~~ -help: if this is intentional, try using `Path::new` instead +help: if this is intentional, consider using `Path::new` | LL | PathBuf::from("/sh"); | ~~~~~~~~~~~~~~~~~~~~ @@ -27,7 +27,7 @@ help: if this is unintentional, try removing the starting separator | LL | path.join("\user"); | ~~~~~~~ -help: if this is intentional, try using `Path::new` instead +help: if this is intentional, consider using `Path::new` | LL | PathBuf::from("\\user"); | ~~~~~~~~~~~~~~~~~~~~~~~ @@ -43,7 +43,7 @@ help: if this is unintentional, try removing the starting separator | LL | path.join("sh"); | ~~~~ -help: if this is intentional, try using `Path::new` instead +help: if this is intentional, consider using `Path::new` | LL | PathBuf::from("/sh"); | ~~~~~~~~~~~~~~~~~~~~ @@ -59,7 +59,7 @@ help: if this is unintentional, try removing the starting separator | LL | path.join(r#"sh"#); | ~~~~~~~ -help: if this is intentional, try using `Path::new` instead +help: if this is intentional, consider using `Path::new` | LL | PathBuf::from(r#"/sh"#); | ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/tools/clippy/tests/ui/manual_ok_or.stderr b/src/tools/clippy/tests/ui/manual_ok_or.stderr index b277d22e59b63..89df6cdbedbcc 100644 --- a/src/tools/clippy/tests/ui/manual_ok_or.stderr +++ b/src/tools/clippy/tests/ui/manual_ok_or.stderr @@ -17,7 +17,7 @@ error: called `map_or(Err(_), Ok)` on an `Option` value --> $DIR/manual_ok_or.rs:14:5 | LL | foo.map_or(Err("error"), Ok); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok_or` instead: `foo.ok_or("error")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok_or`: `foo.ok_or("error")` | = note: `-D clippy::option-map-or-err-ok` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_or_err_ok)]` diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr index dc36a5ee7c692..3ce108b1ca81e 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr @@ -2,7 +2,7 @@ error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:6:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` | = note: `-D clippy::manual-saturating-arithmetic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]` @@ -11,13 +11,13 @@ error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:7:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:8:13 | LL | let _ = 1u8.checked_add(1).unwrap_or(255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u8.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:9:13 @@ -26,49 +26,49 @@ LL | let _ = 1u128 | _____________^ LL | | .checked_add(1) LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); - | |_______________________________________________________________________^ help: try using `saturating_add`: `1u128.saturating_add(1)` + | |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:14:13 | LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_mul`: `1u32.saturating_mul(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:16:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:17:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:18:13 | LL | let _ = 1u8.checked_sub(1).unwrap_or(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u8.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:22:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:23:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:24:13 | LL | let _ = 1i8.checked_add(1).unwrap_or(127); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:25:13 @@ -77,25 +77,25 @@ LL | let _ = 1i128 | _____________^ LL | | .checked_add(1) LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); - | |_______________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(1)` + | |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:28:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:29:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:30:13 | LL | let _ = 1i8.checked_add(-1).unwrap_or(-128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:31:13 @@ -104,25 +104,25 @@ LL | let _ = 1i128 | _____________^ LL | | .checked_add(-1) LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); - | |________________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(-1)` + | |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:38:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:39:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:40:13 | LL | let _ = 1i8.checked_sub(1).unwrap_or(-128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:41:13 @@ -131,25 +131,25 @@ LL | let _ = 1i128 | _____________^ LL | | .checked_sub(1) LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); - | |________________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(1)` + | |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:44:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:45:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:46:13 | LL | let _ = 1i8.checked_sub(-1).unwrap_or(127); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:47:13 @@ -158,7 +158,7 @@ LL | let _ = 1i128 | _____________^ LL | | .checked_sub(-1) LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); - | |_______________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(-1)` + | |_______________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(-1)` error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/map_clone.fixed b/src/tools/clippy/tests/ui/map_clone.fixed index 08b155a1aeaf8..395eea69294dd 100644 --- a/src/tools/clippy/tests/ui/map_clone.fixed +++ b/src/tools/clippy/tests/ui/map_clone.fixed @@ -69,15 +69,48 @@ fn main() { //~^ ERROR: you are explicitly cloning with `.map()` let y = x.cloned(); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method let y = x.cloned(); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method + + let x: Option = Some(0); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.copied(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + let y = x.copied(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + + // Should not suggest `copied` or `cloned` here since `T` is not a reference. + let x: Option = Some(0); + let y = x.map(|x| u32::clone(&x)); + let y = x.map(|x| Clone::clone(&x)); // Testing with `Result` now. let x: Result = Ok(String::new()); let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. let y = x.cloned(); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method + + let x: Result = Ok(0); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.copied(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + let y = x.copied(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + + // Should not suggest `copied` or `cloned` here since `T` is not a reference. + let x: Result = Ok(0); + let y = x.map(|x| u32::clone(&x)); + let y = x.map(|x| Clone::clone(&x)); // We ensure that no warning is emitted here because `useless_asref` is taking over. let x = Some(String::new()); diff --git a/src/tools/clippy/tests/ui/map_clone.rs b/src/tools/clippy/tests/ui/map_clone.rs index 901d9b278b47f..82a103edf5a9c 100644 --- a/src/tools/clippy/tests/ui/map_clone.rs +++ b/src/tools/clippy/tests/ui/map_clone.rs @@ -69,15 +69,48 @@ fn main() { //~^ ERROR: you are explicitly cloning with `.map()` let y = x.map(Clone::clone); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method let y = x.map(String::clone); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method + + let x: Option = Some(0); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.map(|x| u32::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + let y = x.map(|x| Clone::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + + // Should not suggest `copied` or `cloned` here since `T` is not a reference. + let x: Option = Some(0); + let y = x.map(|x| u32::clone(&x)); + let y = x.map(|x| Clone::clone(&x)); // Testing with `Result` now. let x: Result = Ok(String::new()); let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. let y = x.map(|x| String::clone(x)); //~^ ERROR: you are explicitly cloning with `.map()` - let y = x.map(|x| String::clone(x)); + //~| HELP: consider calling the dedicated `cloned` method + let y = x.map(|x| Clone::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method + + let x: Result = Ok(0); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.map(|x| u32::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + let y = x.map(|x| Clone::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + + // Should not suggest `copied` or `cloned` here since `T` is not a reference. + let x: Result = Ok(0); + let y = x.map(|x| u32::clone(&x)); + let y = x.map(|x| Clone::clone(&x)); // We ensure that no warning is emitted here because `useless_asref` is taking over. let x = Some(String::new()); diff --git a/src/tools/clippy/tests/ui/map_clone.stderr b/src/tools/clippy/tests/ui/map_clone.stderr index 9d7e9317b58ce..2c86a67fab8d6 100644 --- a/src/tools/clippy/tests/ui/map_clone.stderr +++ b/src/tools/clippy/tests/ui/map_clone.stderr @@ -50,22 +50,46 @@ LL | let y = x.map(Clone::clone); | ^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> $DIR/map_clone.rs:72:13 + --> $DIR/map_clone.rs:73:13 | LL | let y = x.map(String::clone); | ^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> $DIR/map_clone.rs:78:13 + --> $DIR/map_clone.rs:79:13 | -LL | let y = x.map(|x| String::clone(x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` +LL | let y = x.map(|x| u32::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` error: you are explicitly cloning with `.map()` - --> $DIR/map_clone.rs:80:13 + --> $DIR/map_clone.rs:82:13 + | +LL | let y = x.map(|x| Clone::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:94:13 | LL | let y = x.map(|x| String::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` -error: aborting due to 11 previous errors +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:97:13 + | +LL | let y = x.map(|x| Clone::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:103:13 + | +LL | let y = x.map(|x| u32::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:106:13 + | +LL | let y = x.map(|x| Clone::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index 525a355f40355..3428ff4590602 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -13,7 +13,6 @@ fn foo() -> bool { fn match_same_arms() { let _ = match 42 { 42 => { - //~^ ERROR: this match arm has an identical body to the `_` wildcard arm foo(); let mut a = 42 + [23].len() as i32; if true { @@ -32,6 +31,7 @@ fn match_same_arms() { a }, }; + //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm let _ = match 42 { 42 => foo(), @@ -146,13 +146,13 @@ fn match_same_arms() { empty!(0); }, 1 => { - //~^ ERROR: this match arm has an identical body to another arm empty!(0); }, x => { empty!(x); }, } + //~^^^^^^^ ERROR: this match arm has an identical body to another arm match_expr_like_matches_macro_priority(); } diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index 40b20c7e16d28..512ca7413a795 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -2,9 +2,9 @@ error: this match arm has an identical body to the `_` wildcard arm --> $DIR/match_same_arms2.rs:15:9 | LL | / 42 => { -LL | | LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; +LL | | if true { ... | LL | | a LL | | }, @@ -12,7 +12,7 @@ LL | | }, | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms2.rs:25:9 + --> $DIR/match_same_arms2.rs:24:9 | LL | / _ => { LL | | foo(); @@ -122,7 +122,6 @@ LL | 1 => { | ^ help: try merging the arm patterns: `1 | 0` | _________| | | -LL | | LL | | empty!(0); LL | | }, | |_________^ diff --git a/src/tools/clippy/tests/ui/mem_replace_no_std.fixed b/src/tools/clippy/tests/ui/mem_replace_no_std.fixed new file mode 100644 index 0000000000000..c970f2ba2814b --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace_no_std.fixed @@ -0,0 +1,82 @@ +#![allow(unused)] +#![warn( + clippy::all, + clippy::style, + clippy::mem_replace_option_with_none, + clippy::mem_replace_with_default +)] +#![feature(lang_items)] +#![no_std] + +use core::mem; +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn replace_option_with_none() { + let mut an_option = Some(1); + let _ = an_option.take(); + let an_option = &mut Some(1); + let _ = an_option.take(); +} + +fn replace_with_default() { + let mut refstr = "hello"; + let _ = core::mem::take(&mut refstr); + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = core::mem::take(&mut slice); +} + +// lint is disabled for primitives because in this case `take` +// has no clear benefit over `replace` and sometimes is harder to read +fn dont_lint_primitive() { + let mut pbool = true; + let _ = mem::replace(&mut pbool, false); + + let mut pint = 5; + let _ = mem::replace(&mut pint, 0); +} + +fn main() { + replace_option_with_none(); + replace_with_default(); + dont_lint_primitive(); +} + +fn issue9824() { + struct Foo<'a>(Option<&'a str>); + impl<'a> core::ops::Deref for Foo<'a> { + type Target = Option<&'a str>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<'a> core::ops::DerefMut for Foo<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + struct Bar { + opt: Option, + val: u8, + } + + let mut f = Foo(Some("foo")); + let mut b = Bar { opt: Some(1), val: 12 }; + + // replace option with none + let _ = f.0.take(); + let _ = (*f).take(); + let _ = b.opt.take(); + // replace with default + let _ = mem::replace(&mut b.val, u8::default()); +} diff --git a/src/tools/clippy/tests/ui/mem_replace_no_std.rs b/src/tools/clippy/tests/ui/mem_replace_no_std.rs new file mode 100644 index 0000000000000..673d5c7b4f45a --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace_no_std.rs @@ -0,0 +1,82 @@ +#![allow(unused)] +#![warn( + clippy::all, + clippy::style, + clippy::mem_replace_option_with_none, + clippy::mem_replace_with_default +)] +#![feature(lang_items)] +#![no_std] + +use core::mem; +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn replace_option_with_none() { + let mut an_option = Some(1); + let _ = mem::replace(&mut an_option, None); + let an_option = &mut Some(1); + let _ = mem::replace(an_option, None); +} + +fn replace_with_default() { + let mut refstr = "hello"; + let _ = mem::replace(&mut refstr, ""); + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = mem::replace(&mut slice, &[]); +} + +// lint is disabled for primitives because in this case `take` +// has no clear benefit over `replace` and sometimes is harder to read +fn dont_lint_primitive() { + let mut pbool = true; + let _ = mem::replace(&mut pbool, false); + + let mut pint = 5; + let _ = mem::replace(&mut pint, 0); +} + +fn main() { + replace_option_with_none(); + replace_with_default(); + dont_lint_primitive(); +} + +fn issue9824() { + struct Foo<'a>(Option<&'a str>); + impl<'a> core::ops::Deref for Foo<'a> { + type Target = Option<&'a str>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<'a> core::ops::DerefMut for Foo<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + struct Bar { + opt: Option, + val: u8, + } + + let mut f = Foo(Some("foo")); + let mut b = Bar { opt: Some(1), val: 12 }; + + // replace option with none + let _ = mem::replace(&mut f.0, None); + let _ = mem::replace(&mut *f, None); + let _ = mem::replace(&mut b.opt, None); + // replace with default + let _ = mem::replace(&mut b.val, u8::default()); +} diff --git a/src/tools/clippy/tests/ui/mem_replace_no_std.stderr b/src/tools/clippy/tests/ui/mem_replace_no_std.stderr new file mode 100644 index 0000000000000..744fb5a15876e --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace_no_std.stderr @@ -0,0 +1,50 @@ +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:24:13 + | +LL | let _ = mem::replace(&mut an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` + | + = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_none)]` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:26:13 + | +LL | let _ = mem::replace(an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` + +error: replacing a value of type `T` with `T::default()` is better expressed using `core::mem::take` + --> $DIR/mem_replace_no_std.rs:31:13 + | +LL | let _ = mem::replace(&mut refstr, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut refstr)` + | + = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` + +error: replacing a value of type `T` with `T::default()` is better expressed using `core::mem::take` + --> $DIR/mem_replace_no_std.rs:34:13 + | +LL | let _ = mem::replace(&mut slice, &[]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut slice)` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:77:13 + | +LL | let _ = mem::replace(&mut f.0, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:78:13 + | +LL | let _ = mem::replace(&mut *f, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:79:13 + | +LL | let _ = mem::replace(&mut b.opt, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index 777b1e52c2da6..dabeda72f0c8c 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -181,6 +181,8 @@ fn main() { //~^ ERROR: binding to `_` prefixed variable with no side-effect let _cat = [2, 4, 6, 8][2]; //~^ ERROR: binding to `_` prefixed variable with no side-effect + let _issue_12166 = 42; + let underscore_variable_above_can_be_used_dont_lint = _issue_12166; #[allow(clippy::no_effect)] 0; diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr index f5ba234b4cbf6..8140ba1feee9c 100644 --- a/src/tools/clippy/tests/ui/no_effect.stderr +++ b/src/tools/clippy/tests/ui/no_effect.stderr @@ -152,31 +152,31 @@ LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:175:5 + --> $DIR/no_effect.rs:175:9 | LL | let _unused = 1; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^ | = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::no_effect_underscore_binding)]` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:178:5 + --> $DIR/no_effect.rs:178:9 | LL | let _penguin = || println!("Some helpful closure"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:180:5 + --> $DIR/no_effect.rs:180:9 | LL | let _duck = Struct { field: 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:182:5 + --> $DIR/no_effect.rs:182:9 | LL | let _cat = [2, 4, 6, 8][2]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/open_options.rs b/src/tools/clippy/tests/ui/open_options.rs index 0cdc5bf2bb592..4acb3780df43e 100644 --- a/src/tools/clippy/tests/ui/open_options.rs +++ b/src/tools/clippy/tests/ui/open_options.rs @@ -1,7 +1,18 @@ +#![allow(unused_must_use)] +#![warn(clippy::nonsensical_open_options)] + use std::fs::OpenOptions; -#[allow(unused_must_use)] -#[warn(clippy::nonsensical_open_options)] +trait OpenOptionsExt { + fn truncate_write(&mut self, opt: bool) -> &mut Self; +} + +impl OpenOptionsExt for OpenOptions { + fn truncate_write(&mut self, opt: bool) -> &mut Self { + self.truncate(opt).write(opt) + } +} + fn main() { OpenOptions::new().read(true).truncate(true).open("foo.txt"); //~^ ERROR: file opened with `truncate` and `read` @@ -11,12 +22,31 @@ fn main() { OpenOptions::new().read(true).read(false).open("foo.txt"); //~^ ERROR: the method `read` is called more than once - OpenOptions::new().create(true).create(false).open("foo.txt"); - //~^ ERROR: the method `create` is called more than once + OpenOptions::new() + .create(true) + .truncate(true) // Ensure we don't trigger suspicious open options by having create without truncate + .create(false) + //~^ ERROR: the method `create` is called more than once + .open("foo.txt"); OpenOptions::new().write(true).write(false).open("foo.txt"); //~^ ERROR: the method `write` is called more than once OpenOptions::new().append(true).append(false).open("foo.txt"); //~^ ERROR: the method `append` is called more than once OpenOptions::new().truncate(true).truncate(false).open("foo.txt"); //~^ ERROR: the method `truncate` is called more than once + + std::fs::File::options().read(true).read(false).open("foo.txt"); + //~^ ERROR: the method `read` is called more than once + + let mut options = std::fs::OpenOptions::new(); + options.read(true); + options.read(false); + // Possible future improvement: recognize open options method call chains across statements. + options.open("foo.txt"); + + let mut options = std::fs::OpenOptions::new(); + options.truncate(true); + options.create(true).open("foo.txt"); + + OpenOptions::new().create(true).truncate_write(true).open("foo.txt"); } diff --git a/src/tools/clippy/tests/ui/open_options.stderr b/src/tools/clippy/tests/ui/open_options.stderr index 7ac826f52fa9d..4f84d1d09f984 100644 --- a/src/tools/clippy/tests/ui/open_options.stderr +++ b/src/tools/clippy/tests/ui/open_options.stderr @@ -1,5 +1,5 @@ error: file opened with `truncate` and `read` - --> $DIR/open_options.rs:6:5 + --> $DIR/open_options.rs:17:5 | LL | OpenOptions::new().read(true).truncate(true).open("foo.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,40 +8,46 @@ LL | OpenOptions::new().read(true).truncate(true).open("foo.txt"); = help: to override `-D warnings` add `#[allow(clippy::nonsensical_open_options)]` error: file opened with `append` and `truncate` - --> $DIR/open_options.rs:9:5 + --> $DIR/open_options.rs:20:5 | LL | OpenOptions::new().append(true).truncate(true).open("foo.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the method `read` is called more than once - --> $DIR/open_options.rs:12:5 + --> $DIR/open_options.rs:23:35 | LL | OpenOptions::new().read(true).read(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ error: the method `create` is called more than once - --> $DIR/open_options.rs:14:5 + --> $DIR/open_options.rs:28:10 | -LL | OpenOptions::new().create(true).create(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | .create(false) + | ^^^^^^^^^^^^^ error: the method `write` is called more than once - --> $DIR/open_options.rs:16:5 + --> $DIR/open_options.rs:31:36 | LL | OpenOptions::new().write(true).write(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: the method `append` is called more than once - --> $DIR/open_options.rs:18:5 + --> $DIR/open_options.rs:33:37 | LL | OpenOptions::new().append(true).append(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ error: the method `truncate` is called more than once - --> $DIR/open_options.rs:20:5 + --> $DIR/open_options.rs:35:39 | LL | OpenOptions::new().truncate(true).truncate(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ + +error: the method `read` is called more than once + --> $DIR/open_options.rs:38:41 + | +LL | std::fs::File::options().read(true).read(false).open("foo.txt"); + | ^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/open_options_fixable.fixed b/src/tools/clippy/tests/ui/open_options_fixable.fixed new file mode 100644 index 0000000000000..90a129a9bdffd --- /dev/null +++ b/src/tools/clippy/tests/ui/open_options_fixable.fixed @@ -0,0 +1,7 @@ +use std::fs::OpenOptions; +#[allow(unused_must_use)] +#[warn(clippy::suspicious_open_options)] +fn main() { + OpenOptions::new().create(true).truncate(true).open("foo.txt"); + //~^ ERROR: file opened with `create`, but `truncate` behavior not defined +} diff --git a/src/tools/clippy/tests/ui/open_options_fixable.rs b/src/tools/clippy/tests/ui/open_options_fixable.rs new file mode 100644 index 0000000000000..3a9e522ba1507 --- /dev/null +++ b/src/tools/clippy/tests/ui/open_options_fixable.rs @@ -0,0 +1,7 @@ +use std::fs::OpenOptions; +#[allow(unused_must_use)] +#[warn(clippy::suspicious_open_options)] +fn main() { + OpenOptions::new().create(true).open("foo.txt"); + //~^ ERROR: file opened with `create`, but `truncate` behavior not defined +} diff --git a/src/tools/clippy/tests/ui/open_options_fixable.stderr b/src/tools/clippy/tests/ui/open_options_fixable.stderr new file mode 100644 index 0000000000000..e327661713bfb --- /dev/null +++ b/src/tools/clippy/tests/ui/open_options_fixable.stderr @@ -0,0 +1,14 @@ +error: file opened with `create`, but `truncate` behavior not defined + --> $DIR/open_options_fixable.rs:5:24 + | +LL | OpenOptions::new().create(true).open("foo.txt"); + | ^^^^^^^^^^^^- help: add: `.truncate(true)` + | + = help: if you intend to overwrite an existing file entirely, call `.truncate(true)` + = help: if you instead know that you may want to keep some parts of the old file, call `.truncate(false)` + = help: alternatively, use `.append(true)` to append to the file instead of overwriting it + = note: `-D clippy::suspicious-open-options` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::suspicious_open_options)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr index 9d173e409abcc..036b8c749e417 100644 --- a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr @@ -2,7 +2,7 @@ error: called `.as_ref().map(Deref::deref)` on an `Option` value --> $DIR/option_as_ref_deref.rs:11:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.clone().as_deref()` | = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_as_ref_deref)]` @@ -15,103 +15,103 @@ LL | let _ = opt.clone() LL | | .as_ref().map( LL | | Deref::deref LL | | ) - | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` + | |_________^ help: consider using as_deref: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an `Option` value --> $DIR/option_as_ref_deref.rs:20:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:22:13 | LL | let _ = opt.as_ref().map(String::as_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an `Option` value --> $DIR/option_as_ref_deref.rs:23:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:24:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an `Option` value --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an `Option` value --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an `Option` value --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an `Option` value --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an `Option` value --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an `Option` value --> $DIR/option_as_ref_deref.rs:33:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an `Option` value --> $DIR/option_as_ref_deref.rs:40:13 | LL | let _ = opt.as_ref().map(|x| &**x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an `Option` value --> $DIR/option_as_ref_deref.rs:41:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.as_deref_mut()` error: called `.as_ref().map(std::ops::Deref::deref)` on an `Option` value --> $DIR/option_as_ref_deref.rs:44:13 | LL | let _ = opt.as_ref().map(std::ops::Deref::deref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_ref().map(String::as_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:56:13 | LL | let _ = opt.as_ref().map(String::as_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/option_map_or_err_ok.stderr b/src/tools/clippy/tests/ui/option_map_or_err_ok.stderr index a193e3c4c49d3..381601cb6b3aa 100644 --- a/src/tools/clippy/tests/ui/option_map_or_err_ok.stderr +++ b/src/tools/clippy/tests/ui/option_map_or_err_ok.stderr @@ -2,7 +2,7 @@ error: called `map_or(Err(_), Ok)` on an `Option` value --> $DIR/option_map_or_err_ok.rs:5:13 | LL | let _ = x.map_or(Err("a"), Ok); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok_or` instead: `x.ok_or("a")` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok_or`: `x.ok_or("a")` | = note: `-D clippy::option-map-or-err-ok` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_or_err_ok)]` diff --git a/src/tools/clippy/tests/ui/option_map_or_none.stderr b/src/tools/clippy/tests/ui/option_map_or_none.stderr index f2cfc3f9a2815..d58ff83c3da27 100644 --- a/src/tools/clippy/tests/ui/option_map_or_none.stderr +++ b/src/tools/clippy/tests/ui/option_map_or_none.stderr @@ -2,7 +2,7 @@ error: called `map_or(None, ..)` on an `Option` value --> $DIR/option_map_or_none.rs:10:26 | LL | let _: Option = opt.map_or(None, |x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `map` instead: `opt.map(|x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `map`: `opt.map(|x| x + 1)` | = note: `-D clippy::option-map-or-none` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_or_none)]` @@ -14,13 +14,13 @@ LL | let _: Option = opt.map_or(None, |x| { | __________________________^ LL | | Some(x + 1) LL | | }); - | |_________________________^ help: try using `map` instead: `opt.map(|x| x + 1)` + | |_________________________^ help: consider using `map`: `opt.map(|x| x + 1)` error: called `map_or(None, ..)` on an `Option` value --> $DIR/option_map_or_none.rs:17:26 | LL | let _: Option = opt.map_or(None, bar); - | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(bar)` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `and_then`: `opt.and_then(bar)` error: called `map_or(None, ..)` on an `Option` value --> $DIR/option_map_or_none.rs:18:26 @@ -33,7 +33,7 @@ LL | | Some(offset + height) LL | | }); | |______^ | -help: try using `and_then` instead +help: consider using `and_then` | LL ~ let _: Option = opt.and_then(|x| { LL + let offset = 0; @@ -46,7 +46,7 @@ error: called `map_or(None, Some)` on a `Result` value --> $DIR/option_map_or_none.rs:25:26 | LL | let _: Option = r.map_or(None, Some); - | ^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `r.ok()` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `ok`: `r.ok()` | = note: `-D clippy::result-map-or-into-option` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_map_or_into_option)]` diff --git a/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed b/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed new file mode 100644 index 0000000000000..97c8c394c03d4 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_eq_no_std.fixed @@ -0,0 +1,49 @@ +#![warn(clippy::ptr_eq)] +#![no_std] +#![feature(lang_items)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = core::ptr::eq(a, b); + let _ = core::ptr::eq(a, b); + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/src/tools/clippy/tests/ui/ptr_eq_no_std.rs b/src/tools/clippy/tests/ui/ptr_eq_no_std.rs new file mode 100644 index 0000000000000..a7ba9b4d81746 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_eq_no_std.rs @@ -0,0 +1,49 @@ +#![warn(clippy::ptr_eq)] +#![no_std] +#![feature(lang_items)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = a as *const _ as usize == b as *const _ as usize; + let _ = a as *const _ == b as *const _; + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/src/tools/clippy/tests/ui/ptr_eq_no_std.stderr b/src/tools/clippy/tests/ui/ptr_eq_no_std.stderr new file mode 100644 index 0000000000000..3e289f5be613e --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_eq_no_std.stderr @@ -0,0 +1,17 @@ +error: use `core::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq_no_std.rs:31:13 + | +LL | let _ = a as *const _ as usize == b as *const _ as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` + | + = note: `-D clippy::ptr-eq` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ptr_eq)]` + +error: use `core::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq_no_std.rs:32:13 + | +LL | let _ = a as *const _ == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/read_zero_byte_vec.rs b/src/tools/clippy/tests/ui/read_zero_byte_vec.rs index 76b9b98185119..fd5a88a37a66c 100644 --- a/src/tools/clippy/tests/ui/read_zero_byte_vec.rs +++ b/src/tools/clippy/tests/ui/read_zero_byte_vec.rs @@ -55,14 +55,6 @@ fn test() -> io::Result<()> { let mut buf = [0u8; 100]; f.read(&mut buf)?; - // should not lint - let mut empty = vec![]; - let mut data7 = vec![]; - f.read(&mut empty); - - // should not lint - f.read(&mut data7); - // should not lint let mut data8 = Vec::new(); data8.resize(100, 0); @@ -75,6 +67,27 @@ fn test() -> io::Result<()> { Ok(()) } +fn test_nested() -> io::Result<()> { + let cap = 1000; + let mut f = File::open("foo.txt").unwrap(); + + // Issue #9274 + // Should not lint + let mut v = Vec::new(); + { + v.resize(10, 0); + f.read(&mut v)?; + } + + let mut v = Vec::new(); + { + f.read(&mut v)?; + //~^ ERROR: reading zero byte data to `Vec` + } + + Ok(()) +} + async fn test_futures(r: &mut R) { // should lint let mut data = Vec::new(); diff --git a/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr b/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr index 523ecb2948df3..e85aa051c34bc 100644 --- a/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr +++ b/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr @@ -2,7 +2,7 @@ error: reading zero byte data to `Vec` --> $DIR/read_zero_byte_vec.rs:21:5 | LL | f.read_exact(&mut data).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data).unwrap();` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data)` | = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::read_zero_byte_vec)]` @@ -11,19 +11,19 @@ error: reading zero byte data to `Vec` --> $DIR/read_zero_byte_vec.rs:27:5 | LL | f.read_exact(&mut data2)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)?;` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)` error: reading zero byte data to `Vec` --> $DIR/read_zero_byte_vec.rs:32:5 | LL | f.read_exact(&mut data3)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:37:5 + --> $DIR/read_zero_byte_vec.rs:37:13 | LL | let _ = f.read(&mut data4)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` --> $DIR/read_zero_byte_vec.rs:43:9 @@ -38,28 +38,34 @@ LL | f.read(&mut data6) | ^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:81:5 + --> $DIR/read_zero_byte_vec.rs:84:9 + | +LL | f.read(&mut v)?; + | ^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:94:5 | LL | r.read(&mut data).await.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:86:5 + --> $DIR/read_zero_byte_vec.rs:99:5 | LL | r.read_exact(&mut data2).await.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:93:5 + --> $DIR/read_zero_byte_vec.rs:106:5 | LL | r.read(&mut data).await.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:98:5 + --> $DIR/read_zero_byte_vec.rs:111:5 | LL | r.read_exact(&mut data2).await.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.stderr b/src/tools/clippy/tests/ui/result_map_or_into_option.stderr index 3d6bfef48ecab..201868f09efa6 100644 --- a/src/tools/clippy/tests/ui/result_map_or_into_option.stderr +++ b/src/tools/clippy/tests/ui/result_map_or_into_option.stderr @@ -2,7 +2,7 @@ error: called `map_or(None, Some)` on a `Result` value --> $DIR/result_map_or_into_option.rs:5:13 | LL | let _ = opt.map_or(None, Some); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok`: `opt.ok()` | = note: `-D clippy::result-map-or-into-option` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_map_or_into_option)]` @@ -11,13 +11,13 @@ error: called `map_or_else(|_| None, Some)` on a `Result` value --> $DIR/result_map_or_into_option.rs:7:13 | LL | let _ = opt.map_or_else(|_| None, Some); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok`: `opt.ok()` error: called `map_or_else(|_| None, Some)` on a `Result` value --> $DIR/result_map_or_into_option.rs:10:13 | LL | let _ = opt.map_or_else(|_| { None }, Some); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok`: `opt.ok()` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/same_item_push.stderr b/src/tools/clippy/tests/ui/same_item_push.stderr index f519be463695b..f569aab64900a 100644 --- a/src/tools/clippy/tests/ui/same_item_push.stderr +++ b/src/tools/clippy/tests/ui/same_item_push.stderr @@ -4,7 +4,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(item); | ^^^ | - = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) = note: `-D clippy::same-item-push` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::same_item_push)]` @@ -14,7 +14,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(item); | ^^^ | - = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:36:9 @@ -22,7 +22,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(13); | ^^^ | - = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) + = help: consider using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:42:9 @@ -30,7 +30,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(VALUE); | ^^^ | - = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) + = help: consider using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:49:9 @@ -38,7 +38,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(item); | ^^^ | - = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some.stderr b/src/tools/clippy/tests/ui/search_is_some.stderr index a7a47447f61de..9dec8c9caf274 100644 --- a/src/tools/clippy/tests/ui/search_is_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some.stderr @@ -40,7 +40,7 @@ error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some.rs:42:20 | LL | let _ = (0..1).find(some_closure).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(some_closure)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(some_closure)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some.rs:52:13 @@ -82,7 +82,7 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some.rs:79:13 | LL | let _ = (0..1).find(some_closure).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(some_closure)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(some_closure)` error: aborting due to 8 previous errors 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 f33b0430912a1..107f59a97d2f3 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 @@ -2,7 +2,7 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:9:13 | LL | let _ = v.iter().find(|&x| *x < 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x < 0)` | = note: `-D clippy::search-is-some` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` @@ -11,43 +11,43 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:10:13 | LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(|x| **y == x)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:11:13 | LL | let _ = (0..1).find(|x| *x == 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(|x| x == 0)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:12:13 | LL | let _ = v.iter().find(|x| **x == 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 0)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:13:13 | LL | let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:14:13 | LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(1..3).any(|x| [1, 2, 3].contains(&x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:15:13 | LL | let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:16:13 | LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:17:13 @@ -56,91 +56,91 @@ LL | let _ = (1..3) | _____________^ LL | | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1) LL | | .is_none(); - | |__________________^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` + | |__________________^ help: consider using: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` error: called `is_none()` after searching an `Iterator` with `position` --> $DIR/search_is_some_fixable_none.rs:22:13 | LL | let _ = v.iter().position(|&x| x < 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|&x| x < 0)` error: called `is_none()` after searching an `Iterator` with `rposition` --> $DIR/search_is_some_fixable_none.rs:25:13 | LL | let _ = v.iter().rposition(|&x| x < 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|&x| x < 0)` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:31:13 | LL | let _ = "hello world".find("world").is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains("world")` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:32:13 | LL | let _ = "hello world".find(&s2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains(&s2)` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:33:13 | LL | let _ = "hello world".find(&s2[2..]).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains(&s2[2..])` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:35:13 | LL | let _ = s1.find("world").is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains("world")` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:36:13 | LL | let _ = s1.find(&s2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains(&s2)` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:37:13 | LL | let _ = s1.find(&s2[2..]).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains(&s2[2..])` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:39:13 | LL | let _ = s1[2..].find("world").is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains("world")` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:40:13 | LL | let _ = s1[2..].find(&s2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains(&s2)` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:41:13 | LL | let _ = s1[2..].find(&s2[2..]).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains(&s2[2..])` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:57:25 | LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == &cc)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!filter_hand.iter().any(|cc| c == &cc)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:73:30 | LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == cc)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!filter_hand.iter().any(|cc| c == cc)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:84:17 | LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:87:17 @@ -152,7 +152,7 @@ LL | | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2) LL | | .is_none(); | |______________________^ | -help: use `!_.any()` instead +help: consider using | LL ~ let _ = !vfoo LL ~ .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2); @@ -162,49 +162,49 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:95:17 | LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|a| a[0] == 42)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|a| a[0] == 42)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:101:17 | LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:119:17 | LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:120:17 | LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:123:17 | LL | let _ = v.iter().find(|x| deref_enough(**x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| deref_enough(*x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| deref_enough(*x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:124:17 | LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| deref_enough(*x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x: &u32| deref_enough(*x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:127:17 | LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| arg_no_deref(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| arg_no_deref(&x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:129:17 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| arg_no_deref(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x: &u32| arg_no_deref(&x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:149:17 @@ -216,7 +216,7 @@ LL | | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == LL | | .is_none(); | |______________________^ | -help: use `!_.any()` instead +help: consider using | LL ~ let _ = !vfoo LL ~ .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2); @@ -226,61 +226,61 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:165:17 | LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.inner[0].bar == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.inner[0].bar == 2)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:170:17 | LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|x| (**x)[0] == 9)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|x| (**x)[0] == 9)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:183:17 | LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.by_ref(&v.bar))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.by_ref(&v.bar))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:187:17 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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` --> $DIR/search_is_some_fixable_none.rs:188:17 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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` --> $DIR/search_is_some_fixable_none.rs:207:17 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| s[0].is_empty())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| s[0].is_empty())` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:208:17 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| test_string_1(&s[0]))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| test_string_1(&s[0]))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:217:17 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| fp.field.is_power_of_two())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| fp.field.is_power_of_two())` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:218:17 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_1(fp.field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_1(fp.field))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:219:17 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_2(*fp.field))` error: aborting due to 43 previous errors 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 e878e62de07f3..e706ce646923d 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 @@ -2,7 +2,7 @@ error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:9:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| *x < 0)` | = note: `-D clippy::search-is-some` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` @@ -11,43 +11,43 @@ error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:10:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:11:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:12:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:13:20 | LL | let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 1 || x == 3 || x == 5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 1 || x == 3 || x == 5)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:14:20 | LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| [1, 2, 3].contains(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:15:20 | LL | let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0 || [1, 2, 3].contains(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 0 || [1, 2, 3].contains(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:16:20 | LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:18:10 @@ -55,91 +55,91 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1) | __________^ LL | | .is_some(); - | |__________________^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` + | |__________________^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` error: called `is_some()` after searching an `Iterator` with `position` --> $DIR/search_is_some_fixable_some.rs:22:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with `rposition` --> $DIR/search_is_some_fixable_some.rs:25:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:30:27 | LL | let _ = "hello world".find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:31:27 | LL | let _ = "hello world".find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:32:27 | LL | let _ = "hello world".find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:34:16 | LL | let _ = s1.find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:35:16 | LL | let _ = s1.find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:36:16 | LL | let _ = s1.find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:38:21 | LL | let _ = s1[2..].find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:39:21 | LL | let _ = s1[2..].find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:40:21 | LL | let _ = s1[2..].find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:56:44 | LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == &cc)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == &cc)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:72:49 | LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == cc)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == cc)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:83:29 | LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.foo == 1 && v.bar == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.foo == 1 && v.bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:88:14 @@ -147,55 +147,55 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2) | ______________^ LL | | .is_some(); - | |______________________^ help: use `any()` instead: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)` + | |______________________^ help: consider using: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:94:29 | LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|a| a[0] == 42)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|a| a[0] == 42)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:100:29 | LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|sub| sub[1..4].len() == 3)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|sub| sub[1..4].len() == 3)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:118:30 | LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|ppp_x: &&u32| please(ppp_x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|ppp_x: &&u32| please(ppp_x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:119:50 | LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s.len() == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s.len() == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:122:26 | LL | let _ = v.iter().find(|x| deref_enough(**x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| deref_enough(*x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| deref_enough(*x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:123:26 | LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| deref_enough(*x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| deref_enough(*x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:126:26 | LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| arg_no_deref(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| arg_no_deref(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:128:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:150:14 @@ -203,91 +203,91 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2) | ______________^ LL | | .is_some(); - | |______________________^ help: use `any()` instead: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)` + | |______________________^ help: consider using: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:164:29 | LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.inner[0].bar == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.inner[0].bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:169:29 | LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| (**x)[0] == 9)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| (**x)[0] == 9)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:182:29 | LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.by_ref(&v.bar))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.by_ref(&v.bar))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:186:55 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:187:55 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:206:26 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s[0].is_empty())` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:207:26 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| test_string_1(&s[0]))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:216:26 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| fp.field.is_power_of_two())` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:217:26 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_1(fp.field))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:218:26 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_2(*fp.field))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:233:18 | LL | v.iter().find(|x: &&u32| func(x)).is_some() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| func(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| func(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:242:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_impl(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_impl(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:245:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_dyn(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_dyn(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:248:26 | LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` error: aborting due to 47 previous errors diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed index 15cc8d54faab3..8859a68320f00 100644 --- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -80,6 +80,7 @@ fn main() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); @@ -104,6 +105,7 @@ fn msrv_1_54() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); @@ -124,6 +126,7 @@ fn msrv_1_55() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs index 197225ffbd5c9..7b72efb34ff82 100644 --- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs @@ -80,6 +80,7 @@ fn main() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); @@ -104,6 +105,7 @@ fn msrv_1_54() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); @@ -124,6 +126,7 @@ fn msrv_1_55() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr index 05c11cf7f8c61..b6b0d2effa815 100644 --- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr +++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr @@ -14,7 +14,7 @@ LL | t.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:133:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:136:7 | LL | f.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` diff --git a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.fixed b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.fixed index bbcc0de27d115..cdfa5d9cc7804 100644 --- a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.fixed +++ b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.fixed @@ -1,6 +1,11 @@ +//@aux-build:proc_macro_attr.rs + #![warn(clippy::semicolon_if_nothing_returned)] #![allow(clippy::redundant_closure, clippy::uninlined_format_args, clippy::needless_late_init)] +#[macro_use] +extern crate proc_macro_attr; + fn get_unit() {} // the functions below trigger the lint @@ -120,3 +125,32 @@ fn let_else_stmts() { return; }; } + +mod issue12123 { + #[rustfmt::skip] + mod this_triggers { + #[fake_main] + async fn main() { + + } + } + + mod and_this { + #[fake_main] + async fn main() { + println!("hello"); + } + } + + #[rustfmt::skip] + mod maybe_this { + /** */ #[fake_main] + async fn main() { + } + } + + mod but_this_does_not { + #[fake_main] + async fn main() {} + } +} diff --git a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs index fdc9c0c33f5a3..315b7e4f38399 100644 --- a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs +++ b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs @@ -1,6 +1,11 @@ +//@aux-build:proc_macro_attr.rs + #![warn(clippy::semicolon_if_nothing_returned)] #![allow(clippy::redundant_closure, clippy::uninlined_format_args, clippy::needless_late_init)] +#[macro_use] +extern crate proc_macro_attr; + fn get_unit() {} // the functions below trigger the lint @@ -120,3 +125,32 @@ fn let_else_stmts() { return; }; } + +mod issue12123 { + #[rustfmt::skip] + mod this_triggers { + #[fake_main] + async fn main() { + + } + } + + mod and_this { + #[fake_main] + async fn main() { + println!("hello"); + } + } + + #[rustfmt::skip] + mod maybe_this { + /** */ #[fake_main] + async fn main() { + } + } + + mod but_this_does_not { + #[fake_main] + async fn main() {} + } +} diff --git a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr index 66373a13c5690..09c4d12f216c5 100644 --- a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr +++ b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr @@ -1,5 +1,5 @@ error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:8:5 + --> $DIR/semicolon_if_nothing_returned.rs:13:5 | LL | println!("Hello") | ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");` @@ -8,25 +8,25 @@ LL | println!("Hello") = help: to override `-D warnings` add `#[allow(clippy::semicolon_if_nothing_returned)]` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:12:5 + --> $DIR/semicolon_if_nothing_returned.rs:17:5 | LL | get_unit() | ^^^^^^^^^^ help: add a `;` here: `get_unit();` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:17:5 + --> $DIR/semicolon_if_nothing_returned.rs:22:5 | LL | y = x + 1 | ^^^^^^^^^ help: add a `;` here: `y = x + 1;` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:23:9 + --> $DIR/semicolon_if_nothing_returned.rs:28:9 | LL | hello() | ^^^^^^^ help: add a `;` here: `hello();` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:34:9 + --> $DIR/semicolon_if_nothing_returned.rs:39:9 | LL | ptr::drop_in_place(s.as_mut_ptr()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());` diff --git a/src/tools/clippy/tests/ui/single_call_fn.rs b/src/tools/clippy/tests/ui/single_call_fn.rs index 3cc8061647dff..c20214fecccea 100644 --- a/src/tools/clippy/tests/ui/single_call_fn.rs +++ b/src/tools/clippy/tests/ui/single_call_fn.rs @@ -69,6 +69,17 @@ fn e() { #[test] fn k() {} +mod issue12182 { + #[allow(clippy::single_call_fn)] + fn print_foo(text: &str) { + println!("{text}"); + } + + fn use_print_foo() { + print_foo("foo"); + } +} + #[test] fn l() { k(); diff --git a/src/tools/clippy/tests/ui/single_char_pattern.stderr b/src/tools/clippy/tests/ui/single_char_pattern.stderr index 781ab316d9de3..664d6b5a1e9b0 100644 --- a/src/tools/clippy/tests/ui/single_char_pattern.stderr +++ b/src/tools/clippy/tests/ui/single_char_pattern.stderr @@ -2,7 +2,7 @@ error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:7:13 | LL | x.split("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` | = note: `-D clippy::single-char-pattern` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::single_char_pattern)]` @@ -11,235 +11,235 @@ error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:13:13 | LL | x.split("ß"); - | ^^^ help: try using a `char` instead: `'ß'` + | ^^^ help: consider using a `char`: `'ß'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:14:13 | LL | x.split("ℝ"); - | ^^^ help: try using a `char` instead: `'ℝ'` + | ^^^ help: consider using a `char`: `'ℝ'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:15:13 | LL | x.split("💣"); - | ^^^^ help: try using a `char` instead: `'💣'` + | ^^^^ help: consider using a `char`: `'💣'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:18:23 | LL | x.split_inclusive("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:19:16 | LL | x.contains("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:20:19 | LL | x.starts_with("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:21:17 | LL | x.ends_with("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:22:12 | LL | x.find("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:23:13 | LL | x.rfind("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:24:14 | LL | x.rsplit("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:25:24 | LL | x.split_terminator("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:26:25 | LL | x.rsplit_terminator("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:27:17 | LL | x.splitn(2, "x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:28:18 | LL | x.rsplitn(2, "x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:29:18 | LL | x.split_once("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:30:19 | LL | x.rsplit_once("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:31:15 | LL | x.matches("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:32:16 | LL | x.rmatches("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:33:21 | LL | x.match_indices("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:34:22 | LL | x.rmatch_indices("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:35:26 | LL | x.trim_start_matches("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:36:24 | LL | x.trim_end_matches("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:37:20 | LL | x.strip_prefix("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:38:20 | LL | x.strip_suffix("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:39:15 | LL | x.replace("x", "y"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:40:16 | LL | x.replacen("x", "y", 3); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:42:13 | LL | x.split("\n"); - | ^^^^ help: try using a `char` instead: `'\n'` + | ^^^^ help: consider using a `char`: `'\n'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:43:13 | LL | x.split("'"); - | ^^^ help: try using a `char` instead: `'\''` + | ^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:44:13 | LL | x.split("\'"); - | ^^^^ help: try using a `char` instead: `'\''` + | ^^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:46:13 | LL | x.split("\""); - | ^^^^ help: try using a `char` instead: `'"'` + | ^^^^ help: consider using a `char`: `'"'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:51:31 | LL | x.replace(';', ",").split(","); // issue #2978 - | ^^^ help: try using a `char` instead: `','` + | ^^^ help: consider using a `char`: `','` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:52:19 | LL | x.starts_with("\x03"); // issue #2996 - | ^^^^^^ help: try using a `char` instead: `'\x03'` + | ^^^^^^ help: consider using a `char`: `'\x03'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:59:13 | LL | x.split(r"a"); - | ^^^^ help: try using a `char` instead: `'a'` + | ^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:60:13 | LL | x.split(r#"a"#); - | ^^^^^^ help: try using a `char` instead: `'a'` + | ^^^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:61:13 | LL | x.split(r###"a"###); - | ^^^^^^^^^^ help: try using a `char` instead: `'a'` + | ^^^^^^^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:62:13 | LL | x.split(r###"'"###); - | ^^^^^^^^^^ help: try using a `char` instead: `'\''` + | ^^^^^^^^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:63:13 | LL | x.split(r###"#"###); - | ^^^^^^^^^^ help: try using a `char` instead: `'#'` + | ^^^^^^^^^^ help: consider using a `char`: `'#'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:65:13 | LL | x.split(r#"\"#); - | ^^^^^^ help: try using a `char` instead: `'\\'` + | ^^^^^^ help: consider using a `char`: `'\\'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:66:13 | LL | x.split(r"\"); - | ^^^^ help: try using a `char` instead: `'\\'` + | ^^^^ help: consider using a `char`: `'\\'` error: aborting due to 40 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed index 4fca29698e06b..7e2663d734f63 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed @@ -118,4 +118,33 @@ fn bad_trait_object(arg0: &(dyn Any + Send)) { unimplemented!(); } -fn main() {} +trait Proj { + type S; +} + +impl Proj for () { + type S = (); +} + +impl Proj for i32 { + type S = i32; +} + +trait Base { + fn is_base(&self); +} + +trait Derived: Base + Base<()> { + fn is_derived(&self); +} + +fn f(obj: &dyn Derived

) { + obj.is_derived(); + Base::::is_base(obj); + Base::<()>::is_base(obj); +} + +fn main() { + let _x: fn(_) = f::<()>; + let _x: fn(_) = f::; +} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs index f67c8e35ed4cf..fede1671a4363 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -118,4 +118,33 @@ fn bad_trait_object(arg0: &(dyn Any + Send + Send)) { unimplemented!(); } -fn main() {} +trait Proj { + type S; +} + +impl Proj for () { + type S = (); +} + +impl Proj for i32 { + type S = i32; +} + +trait Base { + fn is_base(&self); +} + +trait Derived: Base + Base<()> { + fn is_derived(&self); +} + +fn f(obj: &dyn Derived

) { + obj.is_derived(); + Base::::is_base(obj); + Base::<()>::is_base(obj); +} + +fn main() { + let _x: fn(_) = f::<()>; + let _x: fn(_) = f::; +} diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 32f6027e99111..1796ccaf28e1a 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -102,19 +102,6 @@ fn crosspointer() { } } -#[warn(clippy::transmute_int_to_char)] -fn int_to_char() { - let _: char = unsafe { std::mem::transmute(0_u32) }; - //~^ ERROR: transmute from a `u32` to a `char` - //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` - let _: char = unsafe { std::mem::transmute(0_i32) }; - //~^ ERROR: transmute from a `i32` to a `char` - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - #[warn(clippy::transmute_int_to_bool)] fn int_to_bool() { let _: bool = unsafe { std::mem::transmute(0_u8) }; diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index cdc733b54a9b2..df32d478cbf40 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -88,23 +88,8 @@ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:107:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:110:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` - error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:120:28 + --> $DIR/transmute.rs:107:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -113,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 `u32` to a `f32` - --> $DIR/transmute.rs:128:31 + --> $DIR/transmute.rs:115:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -122,25 +107,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:131:31 + --> $DIR/transmute.rs:118: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` - --> $DIR/transmute.rs:133:31 + --> $DIR/transmute.rs:120: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` - --> $DIR/transmute.rs:135:31 + --> $DIR/transmute.rs:122:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:156:30 + --> $DIR/transmute.rs:143:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -149,85 +134,85 @@ 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]` - --> $DIR/transmute.rs:159:30 + --> $DIR/transmute.rs:146: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]` - --> $DIR/transmute.rs:161:31 + --> $DIR/transmute.rs:148: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]` - --> $DIR/transmute.rs:163:30 + --> $DIR/transmute.rs:150: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]` - --> $DIR/transmute.rs:165:30 + --> $DIR/transmute.rs:152: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]` - --> $DIR/transmute.rs:167:31 + --> $DIR/transmute.rs:154:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:169:30 + --> $DIR/transmute.rs:156: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]` - --> $DIR/transmute.rs:171:30 + --> $DIR/transmute.rs:158: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 `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:177:30 + --> $DIR/transmute.rs:164: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]` - --> $DIR/transmute.rs:179:30 + --> $DIR/transmute.rs:166: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]` - --> $DIR/transmute.rs:181:31 + --> $DIR/transmute.rs:168: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]` - --> $DIR/transmute.rs:183:30 + --> $DIR/transmute.rs:170: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]` - --> $DIR/transmute.rs:185:30 + --> $DIR/transmute.rs:172: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]` - --> $DIR/transmute.rs:187:31 + --> $DIR/transmute.rs:174:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:198:28 + --> $DIR/transmute.rs:185:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -236,16 +221,16 @@ 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` - --> $DIR/transmute.rs:201:32 + --> $DIR/transmute.rs:188: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` - --> $DIR/transmute.rs:203:30 + --> $DIR/transmute.rs:190:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 38 previous errors +error: aborting due to 36 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed new file mode 100644 index 0000000000000..1708011817541 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed @@ -0,0 +1,15 @@ +#![warn(clippy::transmute_int_to_char)] + +fn int_to_char() { + let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; + //~^ ERROR: transmute from a `u32` to a `char` + //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` + let _: char = unsafe { std::char::from_u32(0_i32 as u32).unwrap() }; + //~^ ERROR: transmute from a `i32` to a `char` + + // These shouldn't warn + const _: char = unsafe { std::mem::transmute(0_u32) }; + const _: char = unsafe { std::mem::transmute(0_i32) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.rs b/src/tools/clippy/tests/ui/transmute_int_to_char.rs new file mode 100644 index 0000000000000..5846a97e88abf --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.rs @@ -0,0 +1,15 @@ +#![warn(clippy::transmute_int_to_char)] + +fn int_to_char() { + let _: char = unsafe { std::mem::transmute(0_u32) }; + //~^ ERROR: transmute from a `u32` to a `char` + //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` + let _: char = unsafe { std::mem::transmute(0_i32) }; + //~^ ERROR: transmute from a `i32` to a `char` + + // These shouldn't warn + const _: char = unsafe { std::mem::transmute(0_u32) }; + const _: char = unsafe { std::mem::transmute(0_i32) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.stderr b/src/tools/clippy/tests/ui/transmute_int_to_char.stderr new file mode 100644 index 0000000000000..2297f5b4f8bf2 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.stderr @@ -0,0 +1,17 @@ +error: transmute from a `u32` to a `char` + --> $DIR/transmute_int_to_char.rs:4:28 + | +LL | let _: char = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` + | + = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` + +error: transmute from a `i32` to a `char` + --> $DIR/transmute_int_to_char.rs:7:28 + | +LL | let _: char = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` + +error: aborting due to 2 previous errors + 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 new file mode 100644 index 0000000000000..9ae4e11fb56e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed @@ -0,0 +1,27 @@ +#![no_std] +#![feature(lang_items)] +#![warn(clippy::transmute_int_to_char)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn int_to_char() { + let _: char = unsafe { core::char::from_u32(0_u32).unwrap() }; + //~^ ERROR: transmute from a `u32` to a `char` + //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` + let _: char = unsafe { core::char::from_u32(0_i32 as u32).unwrap() }; + //~^ ERROR: transmute from a `i32` to a `char` + + // These shouldn't warn + const _: char = unsafe { core::mem::transmute(0_u32) }; + const _: char = unsafe { core::mem::transmute(0_i32) }; +} + +fn main() {} 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 new file mode 100644 index 0000000000000..9a2afd5bd2fd6 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs @@ -0,0 +1,27 @@ +#![no_std] +#![feature(lang_items)] +#![warn(clippy::transmute_int_to_char)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn int_to_char() { + let _: char = unsafe { core::mem::transmute(0_u32) }; + //~^ ERROR: transmute from a `u32` to a `char` + //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` + let _: char = unsafe { core::mem::transmute(0_i32) }; + //~^ ERROR: transmute from a `i32` to a `char` + + // These shouldn't warn + const _: char = unsafe { core::mem::transmute(0_u32) }; + const _: char = unsafe { core::mem::transmute(0_i32) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr new file mode 100644 index 0000000000000..aace6968696da --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.stderr @@ -0,0 +1,17 @@ +error: transmute from a `u32` to a `char` + --> $DIR/transmute_int_to_char_no_std.rs:16:28 + | +LL | let _: char = unsafe { core::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_u32).unwrap()` + | + = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` + +error: transmute from a `i32` to a `char` + --> $DIR/transmute_int_to_char_no_std.rs:19:28 + | +LL | let _: char = unsafe { core::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_i32 as u32).unwrap()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_ref_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ref_to_ref.rs index e7f35c5743684..bdc7b9f647877 100644 --- a/src/tools/clippy/tests/ui/transmute_ref_to_ref.rs +++ b/src/tools/clippy/tests/ui/transmute_ref_to_ref.rs @@ -12,7 +12,7 @@ fn main() { let b: &[u8] = unsafe { std::mem::transmute(a) }; //~^ ERROR: transmute from a reference to a reference let bytes = &[1u8, 2u8, 3u8, 4u8] as &[u8]; - let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; + let alt_slice: &[u32] = unsafe { std::mem::transmute(bytes) }; //~^ ERROR: transmute from a reference to a reference } } diff --git a/src/tools/clippy/tests/ui/transmute_ref_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ref_to_ref.stderr index cc6b156b18885..4238bc637ad04 100644 --- a/src/tools/clippy/tests/ui/transmute_ref_to_ref.stderr +++ b/src/tools/clippy/tests/ui/transmute_ref_to_ref.stderr @@ -19,8 +19,8 @@ LL | let b: &[u8] = unsafe { std::mem::transmute(a) }; error: transmute from a reference to a reference --> $DIR/transmute_ref_to_ref.rs:15:42 | -LL | let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` +LL | let alt_slice: &[u32] = unsafe { std::mem::transmute(bytes) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.rs b/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.rs new file mode 100644 index 0000000000000..b67386f858852 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.rs @@ -0,0 +1,30 @@ +//@no-rustfix + +#![deny(clippy::transmute_ptr_to_ptr)] +#![allow(dead_code)] +#![feature(lang_items)] +#![no_std] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn main() { + unsafe { + let single_u64: &[u64] = &[0xDEAD_BEEF_DEAD_BEEF]; + let bools: &[bool] = unsafe { core::mem::transmute(single_u64) }; + //~^ ERROR: transmute from a reference to a reference + let a: &[u32] = &[0x12345678, 0x90ABCDEF, 0xFEDCBA09, 0x87654321]; + let b: &[u8] = unsafe { core::mem::transmute(a) }; + //~^ ERROR: transmute from a reference to a reference + let bytes = &[1u8, 2u8, 3u8, 4u8] as &[u8]; + let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; + //~^ ERROR: transmute from a reference to a reference + } +} diff --git a/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.stderr b/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.stderr new file mode 100644 index 0000000000000..ca7966ffa9e88 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ref_to_ref_no_std.stderr @@ -0,0 +1,26 @@ +error: transmute from a reference to a reference + --> $DIR/transmute_ref_to_ref_no_std.rs:21:39 + | +LL | let bools: &[bool] = unsafe { core::mem::transmute(single_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(single_u64 as *const [u64] as *const [bool])` + | +note: the lint level is defined here + --> $DIR/transmute_ref_to_ref_no_std.rs:3:9 + | +LL | #![deny(clippy::transmute_ptr_to_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a reference to a reference + --> $DIR/transmute_ref_to_ref_no_std.rs:24:33 + | +LL | let b: &[u8] = unsafe { core::mem::transmute(a) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const [u32] as *const [u8])` + +error: transmute from a reference to a reference + --> $DIR/transmute_ref_to_ref_no_std.rs:27:42 + | +LL | let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.rs b/src/tools/clippy/tests/ui/unconditional_recursion.rs index e1a2d6a90b896..7b898a6e0e784 100644 --- a/src/tools/clippy/tests/ui/unconditional_recursion.rs +++ b/src/tools/clippy/tests/ui/unconditional_recursion.rs @@ -264,6 +264,28 @@ impl S13 { } } -fn main() { - // test code goes here +struct S14 { + field: String, +} + +impl PartialEq for S14 { + fn eq(&self, other: &Self) -> bool { + // Should not warn! + self.field.eq(&other.field) + } +} + +struct S15<'a> { + field: &'a S15<'a>, } + +impl PartialEq for S15<'_> { + fn eq(&self, other: &Self) -> bool { + //~^ ERROR: function cannot return without recursing + let mine = &self.field; + let theirs = &other.field; + mine.eq(theirs) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.stderr b/src/tools/clippy/tests/ui/unconditional_recursion.stderr index 5d82e2a9f3194..094b80d4586c8 100644 --- a/src/tools/clippy/tests/ui/unconditional_recursion.stderr +++ b/src/tools/clippy/tests/ui/unconditional_recursion.stderr @@ -340,5 +340,22 @@ note: recursive call site LL | Self::default() | ^^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:283:5 + | +LL | / fn eq(&self, other: &Self) -> bool { +LL | | +LL | | let mine = &self.field; +LL | | let theirs = &other.field; +LL | | mine.eq(theirs) +LL | | } + | |_____^ + | +note: recursive call site + --> $DIR/unconditional_recursion.rs:287:9 + | +LL | mine.eq(theirs) + | ^^^^^^^^^^^^^^^ + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_join.stderr b/src/tools/clippy/tests/ui/unnecessary_join.stderr index 8bf2ac5fdb1d9..205a714b694ae 100644 --- a/src/tools/clippy/tests/ui/unnecessary_join.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_join.stderr @@ -4,7 +4,7 @@ error: called `.collect::>().join("")` on an iterator LL | .collect::>() | __________^ LL | | .join(""); - | |_________________^ help: try using: `collect::()` + | |_________________^ help: consider using: `collect::()` | = note: `-D clippy::unnecessary-join` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_join)]` @@ -15,7 +15,7 @@ error: called `.collect::>().join("")` on an iterator LL | .collect::>() | __________^ LL | | .join(""); - | |_________________^ help: try using: `collect::()` + | |_________________^ help: consider using: `collect::()` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr index 9d54c8d50e31f..f4409113a45fa 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr @@ -1,4 +1,4 @@ -error: use Vec::sort here instead +error: consider using `sort` --> $DIR/unnecessary_sort_by.rs:12:5 | LL | vec.sort_by(|a, b| a.cmp(b)); @@ -7,67 +7,67 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_sort_by)]` -error: use Vec::sort here instead +error: consider using `sort` --> $DIR/unnecessary_sort_by.rs:13:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:15:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs()))` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:19:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b)))` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:29:5 | LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:30:5 | LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:89:9 | LL | args.sort_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:90:9 | LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:92:9 | LL | args.sort_by(|a, b| b.name().cmp(&a.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| std::cmp::Reverse(b.name()))` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:93:9 | LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs index 62aec6e9eafca..9974600dad5be 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.rs +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -1,4 +1,5 @@ #![allow(dead_code, clippy::needless_pass_by_ref_mut)] +#![allow(clippy::redundant_pattern_matching)] #![warn(clippy::unused_io_amount)] extern crate futures; @@ -142,4 +143,90 @@ async fn undetected_bad_async_write(w: &mut W) { future.await.unwrap(); } +fn match_okay_underscore(s: &mut T) { + match s.write(b"test") { + //~^ ERROR: written amount is not handled + Ok(_) => todo!(), + //~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled + Err(_) => todo!(), + }; + + let mut buf = [0u8; 4]; + match s.read(&mut buf) { + //~^ ERROR: read amount is not handled + Ok(_) => todo!(), + //~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled + Err(_) => todo!(), + } +} + +fn match_okay_underscore_read_expr(s: &mut T) { + match s.read(&mut [0u8; 4]) { + //~^ ERROR: read amount is not handled + Ok(_) => todo!(), + //~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled + Err(_) => todo!(), + } +} + +fn match_okay_underscore_write_expr(s: &mut T) { + match s.write(b"test") { + //~^ ERROR: written amount is not handled + Ok(_) => todo!(), + //~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled + Err(_) => todo!(), + } +} + +fn returned_value_should_not_lint(s: &mut T) -> Result { + s.write(b"test") +} + +fn if_okay_underscore_read_expr(s: &mut T) { + if let Ok(_) = s.read(&mut [0u8; 4]) { + //~^ ERROR: read amount is not handled + todo!() + } +} + +fn if_okay_underscore_write_expr(s: &mut T) { + if let Ok(_) = s.write(b"test") { + //~^ ERROR: written amount is not handled + todo!() + } +} + +fn if_okay_dots_write_expr(s: &mut T) { + if let Ok(..) = s.write(b"test") { + //~^ ERROR: written amount is not handled + todo!() + } +} + +fn if_okay_underscore_write_expr_true_negative(s: &mut T) { + if let Ok(bound) = s.write(b"test") { + todo!() + } +} + +fn match_okay_underscore_true_neg(s: &mut T) { + match s.write(b"test") { + Ok(bound) => todo!(), + Err(_) => todo!(), + }; +} + +fn true_negative(s: &mut T) { + let mut buf = [0u8; 4]; + let read_amount = s.read(&mut buf).unwrap(); +} + +fn on_return_should_not_raise(s: &mut T) -> io::Result { + /// this is bad code because it goes around the problem of handling the read amount + /// by returning it, which makes it impossible to know this is a resonpose from the + /// correct account. + let mut buf = [0u8; 4]; + s.read(&mut buf) +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr index f9aef596a1c98..4af56d264bfa1 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.stderr +++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr @@ -1,5 +1,5 @@ error: written amount is not handled - --> $DIR/unused_io_amount.rs:9:5 + --> $DIR/unused_io_amount.rs:10:5 | LL | s.write(b"test")?; | ^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | s.write(b"test")?; = help: to override `-D warnings` add `#[allow(clippy::unused_io_amount)]` error: read amount is not handled - --> $DIR/unused_io_amount.rs:12:5 + --> $DIR/unused_io_amount.rs:13:5 | LL | s.read(&mut buf)?; | ^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | s.read(&mut buf)?; = help: use `Read::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:18:5 + --> $DIR/unused_io_amount.rs:19:5 | LL | s.write(b"test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | s.write(b"test").unwrap(); = help: use `Write::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:21:5 + --> $DIR/unused_io_amount.rs:22:5 | LL | s.read(&mut buf).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,19 +33,19 @@ LL | s.read(&mut buf).unwrap(); = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:26:5 + --> $DIR/unused_io_amount.rs:27:5 | LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: written amount is not handled - --> $DIR/unused_io_amount.rs:28:5 + --> $DIR/unused_io_amount.rs:29:5 | LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: read amount is not handled - --> $DIR/unused_io_amount.rs:36:5 + --> $DIR/unused_io_amount.rs:37:5 | LL | reader.read(&mut result).ok()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | reader.read(&mut result).ok()?; = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:46:5 + --> $DIR/unused_io_amount.rs:47:5 | LL | reader.read(&mut result).or_else(|err| Err(err))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL | reader.read(&mut result).or_else(|err| Err(err))?; = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:59:5 + --> $DIR/unused_io_amount.rs:60:5 | LL | reader.read(&mut result).or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL | reader.read(&mut result).or(Err(Error::Kind))?; = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:67:5 + --> $DIR/unused_io_amount.rs:68:5 | LL | / reader LL | | @@ -82,7 +82,7 @@ LL | | .expect("error"); = help: use `Read::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:77:5 + --> $DIR/unused_io_amount.rs:78:5 | LL | s.write(b"ok").is_ok(); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | s.write(b"ok").is_ok(); = help: use `Write::write_all` instead, or handle partial writes error: written amount is not handled - --> $DIR/unused_io_amount.rs:79:5 + --> $DIR/unused_io_amount.rs:80:5 | LL | s.write(b"err").is_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -98,7 +98,7 @@ LL | s.write(b"err").is_err(); = help: use `Write::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:82:5 + --> $DIR/unused_io_amount.rs:83:5 | LL | s.read(&mut buf).is_ok(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL | s.read(&mut buf).is_ok(); = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:84:5 + --> $DIR/unused_io_amount.rs:85:5 | LL | s.read(&mut buf).is_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ LL | s.read(&mut buf).is_err(); = help: use `Read::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:89:5 + --> $DIR/unused_io_amount.rs:90:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:95:5 + --> $DIR/unused_io_amount.rs:96:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -130,7 +130,15 @@ LL | r.read(&mut buf[..]).await.unwrap(); = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:109:9 + --> $DIR/unused_io_amount.rs:104:5 + | +LL | w.write(b"hello world"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:110:9 | LL | w.write(b"hello world").await?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +146,7 @@ LL | w.write(b"hello world").await?; = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:118:9 + --> $DIR/unused_io_amount.rs:119:9 | LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,7 +154,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:127:5 + --> $DIR/unused_io_amount.rs:128:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,12 +162,103 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:133:5 + --> $DIR/unused_io_amount.rs:134:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 20 previous errors +error: written amount is not handled + --> $DIR/unused_io_amount.rs:147:11 + | +LL | match s.write(b"test") { + | ^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:149:9 + | +LL | Ok(_) => todo!(), + | ^^^^^^^^^^^^^^^^ + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:155:11 + | +LL | match s.read(&mut buf) { + | ^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:157:9 + | +LL | Ok(_) => todo!(), + | ^^^^^^^^^^^^^^^^ + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:164:11 + | +LL | match s.read(&mut [0u8; 4]) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:166:9 + | +LL | Ok(_) => todo!(), + | ^^^^^^^^^^^^^^^^ + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:173:11 + | +LL | match s.write(b"test") { + | ^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:175:9 + | +LL | Ok(_) => todo!(), + | ^^^^^^^^^^^^^^^^ + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:186:8 + | +LL | if let Ok(_) = s.read(&mut [0u8; 4]) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:186:12 + | +LL | if let Ok(_) = s.read(&mut [0u8; 4]) { + | ^^^^^ + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:193:8 + | +LL | if let Ok(_) = s.write(b"test") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:193:12 + | +LL | if let Ok(_) = s.write(b"test") { + | ^^^^^ + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:200:8 + | +LL | if let Ok(..) = s.write(b"test") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:200:12 + | +LL | if let Ok(..) = s.write(b"test") { + | ^^^^^^ + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed index 88b95095bc0f0..c98f2928e03cc 100644 --- a/src/tools/clippy/tests/ui/useless_asref.fixed +++ b/src/tools/clippy/tests/ui/useless_asref.fixed @@ -144,6 +144,42 @@ fn foo() { //~^ ERROR: this call to `as_ref.map(...)` does nothing } +mod issue12135 { + pub struct Struct { + field: Option, + } + + #[derive(Clone)] + pub struct Foo; + + #[derive(Clone)] + struct InnerStruct { + x: Foo, + } + + impl InnerStruct { + fn method(&self) -> &Foo { + &self.x + } + } + + pub fn f(x: &Struct) -> Option { + x.field.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + x.field.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + x.field.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + + // https://github.com/rust-lang/rust-clippy/pull/12136#discussion_r1451565223 + #[allow(clippy::clone_on_copy)] + Some(1).clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + + x.field.as_ref().map(|v| v.method().clone()) + } +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs index 504dc1f5cbf90..f9d603f116de5 100644 --- a/src/tools/clippy/tests/ui/useless_asref.rs +++ b/src/tools/clippy/tests/ui/useless_asref.rs @@ -144,6 +144,42 @@ fn foo() { //~^ ERROR: this call to `as_ref.map(...)` does nothing } +mod issue12135 { + pub struct Struct { + field: Option, + } + + #[derive(Clone)] + pub struct Foo; + + #[derive(Clone)] + struct InnerStruct { + x: Foo, + } + + impl InnerStruct { + fn method(&self) -> &Foo { + &self.x + } + } + + pub fn f(x: &Struct) -> Option { + x.field.as_ref().map(|v| v.clone()); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + x.field.as_ref().map(Clone::clone); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + x.field.as_ref().map(|v| Clone::clone(v)); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + + // https://github.com/rust-lang/rust-clippy/pull/12136#discussion_r1451565223 + #[allow(clippy::clone_on_copy)] + Some(1).as_ref().map(|&x| x.clone()); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + + x.field.as_ref().map(|v| v.method().clone()) + } +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.stderr b/src/tools/clippy/tests/ui/useless_asref.stderr index deb5d90f2f62f..e158df2664d77 100644 --- a/src/tools/clippy/tests/ui/useless_asref.stderr +++ b/src/tools/clippy/tests/ui/useless_asref.stderr @@ -88,5 +88,29 @@ error: this call to `as_ref.map(...)` does nothing LL | let z = x.as_ref().map(|z| String::clone(z)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` -error: aborting due to 14 previous errors +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:167:9 + | +LL | x.field.as_ref().map(|v| v.clone()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:169:9 + | +LL | x.field.as_ref().map(Clone::clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:171:9 + | +LL | x.field.as_ref().map(|v| Clone::clone(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:176:9 + | +LL | Some(1).as_ref().map(|&x| x.clone()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(1).clone()` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index a05765b398133..3eadc66f544f3 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -19,7 +19,6 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" -users_on_vacation = ["blyxyas"] [assign.owners] "/.github" = ["@flip1995"] diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index aa25f82ae1d8d..bf1feb9a7eb07 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -379,14 +379,14 @@ fn unescape_string_error_message(text: &str, mode: Mode) -> &'static str { let mut error_message = ""; match mode { Mode::CStr => { - rustc_lexer::unescape::unescape_c_string(text, mode, &mut |_, res| { + rustc_lexer::unescape::unescape_mixed(text, mode, &mut |_, res| { if let Err(e) = res { error_message = error_to_diagnostic_message(e, mode); } }); } Mode::ByteStr | Mode::Str => { - rustc_lexer::unescape::unescape_literal(text, mode, &mut |_, res| { + rustc_lexer::unescape::unescape_unicode(text, mode, &mut |_, res| { if let Err(e) = res { error_message = error_to_diagnostic_message(e, mode); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index b39006e2ff265..7cd1f1550b988 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -6,7 +6,7 @@ use std::{ }; use rustc_lexer::unescape::{ - unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit, Mode, + unescape_byte, unescape_char, unescape_mixed, unescape_unicode, MixedUnit, Mode, }; use crate::{ @@ -193,7 +193,7 @@ pub trait IsString: AstToken { let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; - unescape_literal(text, Self::MODE, &mut |range, unescaped_char| { + unescape_unicode(text, Self::MODE, &mut |range, unescaped_char| { let text_range = TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()); cb(text_range + offset, unescaped_char); @@ -226,7 +226,7 @@ impl ast::String { let mut buf = String::new(); let mut prev_end = 0; let mut has_error = false; - unescape_literal(text, Self::MODE, &mut |char_range, unescaped_char| match ( + unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match ( unescaped_char, buf.capacity() == 0, ) { @@ -270,7 +270,7 @@ impl ast::ByteString { let mut buf: Vec = Vec::new(); let mut prev_end = 0; let mut has_error = false; - unescape_literal(text, Self::MODE, &mut |char_range, unescaped_char| match ( + unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match ( unescaped_char, buf.capacity() == 0, ) { @@ -311,7 +311,7 @@ impl IsString for ast::CString { let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; - unescape_c_string(text, Self::MODE, &mut |range, unescaped_char| { + unescape_mixed(text, Self::MODE, &mut |range, unescaped_char| { let text_range = TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()); // XXX: This method should only be used for highlighting ranges. The unescaped @@ -336,12 +336,11 @@ impl ast::CString { let mut buf = Vec::new(); let mut prev_end = 0; let mut has_error = false; - let mut char_buf = [0u8; 4]; - let mut extend_unit = |buf: &mut Vec, unit: CStrUnit| match unit { - CStrUnit::Byte(b) => buf.push(b), - CStrUnit::Char(c) => buf.extend(c.encode_utf8(&mut char_buf).as_bytes()), + let extend_unit = |buf: &mut Vec, unit: MixedUnit| match unit { + MixedUnit::Char(c) => buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()), + MixedUnit::HighByte(b) => buf.push(b), }; - unescape_c_string(text, Self::MODE, &mut |char_range, unescaped| match ( + unescape_mixed(text, Self::MODE, &mut |char_range, unescaped| match ( unescaped, buf.capacity() == 0, ) { diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs index 69dffbf79f191..5c5b26f525f66 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs @@ -5,7 +5,7 @@ mod block; use rowan::Direction; -use rustc_lexer::unescape::{self, unescape_literal, Mode}; +use rustc_lexer::unescape::{self, unescape_mixed, unescape_unicode, Mode}; use crate::{ algo, @@ -140,7 +140,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { ast::LiteralKind::String(s) => { if !s.is_raw() { if let Some(without_quotes) = unquote(text, 1, '"') { - unescape_literal(without_quotes, Mode::Str, &mut |range, char| { + unescape_unicode(without_quotes, Mode::Str, &mut |range, char| { if let Err(err) = char { push_err(1, range.start, err); } @@ -151,7 +151,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { ast::LiteralKind::ByteString(s) => { if !s.is_raw() { if let Some(without_quotes) = unquote(text, 2, '"') { - unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| { + unescape_unicode(without_quotes, Mode::ByteStr, &mut |range, char| { if let Err(err) = char { push_err(1, range.start, err); } @@ -162,7 +162,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { ast::LiteralKind::CString(s) => { if !s.is_raw() { if let Some(without_quotes) = unquote(text, 2, '"') { - unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| { + unescape_mixed(without_quotes, Mode::CStr, &mut |range, char| { if let Err(err) = char { push_err(1, range.start, err); } @@ -172,7 +172,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { } ast::LiteralKind::Char(_) => { if let Some(without_quotes) = unquote(text, 1, '\'') { - unescape_literal(without_quotes, Mode::Char, &mut |range, char| { + unescape_unicode(without_quotes, Mode::Char, &mut |range, char| { if let Err(err) = char { push_err(1, range.start, err); } @@ -181,7 +181,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { } ast::LiteralKind::Byte(_) => { if let Some(without_quotes) = unquote(text, 2, '\'') { - unescape_literal(without_quotes, Mode::Byte, &mut |range, char| { + unescape_unicode(without_quotes, Mode::Byte, &mut |range, char| { if let Err(err) = char { push_err(2, range.start, err); } diff --git a/tests/ui/async-await/in-trait/returning-possibly-unsized-self.rs b/tests/ui/async-await/in-trait/returning-possibly-unsized-self.rs new file mode 100644 index 0000000000000..72f02679d7712 --- /dev/null +++ b/tests/ui/async-await/in-trait/returning-possibly-unsized-self.rs @@ -0,0 +1,18 @@ +// check-pass +// edition:2021 + +#![deny(opaque_hidden_inferred_bound)] + +trait Repository /* : ?Sized */ { + async fn new() -> Self; +} + +struct MyRepository {} + +impl Repository for MyRepository { + async fn new() -> Self { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/internal/internal-unstable.rs b/tests/ui/internal/internal-unstable.rs index 1eb27fbdc3a63..a4445fefef57d 100644 --- a/tests/ui/internal/internal-unstable.rs +++ b/tests/ui/internal/internal-unstable.rs @@ -28,6 +28,14 @@ macro_rules! bar { }} } +#[allow_internal_unstable(stmt_expr_attributes)] +macro_rules! internal_attr { + ($e: expr) => { + #[allow(overflowing_literals)] + $e + } +} + fn main() { // ok, the instability is contained. call_unstable_allow!(); @@ -51,4 +59,6 @@ fn main() { #[allow_internal_unstable] _ => {} } + + assert_eq!(internal_attr!(1e100_f32), f32::INFINITY); } diff --git a/tests/ui/internal/internal-unstable.stderr b/tests/ui/internal/internal-unstable.stderr index af5ac21e69640..78b9109d1c287 100644 --- a/tests/ui/internal/internal-unstable.stderr +++ b/tests/ui/internal/internal-unstable.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature 'function' - --> $DIR/internal-unstable.rs:40:25 + --> $DIR/internal-unstable.rs:48:25 | LL | pass_through_allow!(internal_unstable::unstable()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pass_through_allow!(internal_unstable::unstable()); = 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 'function' - --> $DIR/internal-unstable.rs:42:27 + --> $DIR/internal-unstable.rs:50:27 | LL | pass_through_noallow!(internal_unstable::unstable()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | pass_through_noallow!(internal_unstable::unstable()); = 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 'function' - --> $DIR/internal-unstable.rs:46:22 + --> $DIR/internal-unstable.rs:54:22 | LL | println!("{:?}", internal_unstable::unstable()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | println!("{:?}", internal_unstable::unstable()); = 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 'function' - --> $DIR/internal-unstable.rs:48:10 + --> $DIR/internal-unstable.rs:56:10 | LL | bar!(internal_unstable::unstable()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/str/str-escape.rs b/tests/ui/str/str-escape.rs index 10a72421f24a7..89a8217106391 100644 --- a/tests/ui/str/str-escape.rs +++ b/tests/ui/str/str-escape.rs @@ -1,5 +1,6 @@ // check-pass // ignore-tidy-tab +// edition: 2021 fn main() { let s = "\ @@ -8,11 +9,11 @@ fn main() { //~^^^ WARNING multiple lines skipped by escaped newline assert_eq!(s, ""); - let s = "foo\ + let s = c"foo\   bar "; //~^^^ WARNING whitespace symbol '\u{a0}' is not skipped - assert_eq!(s, "foo  bar\n "); + assert_eq!(s, c"foo  bar\n "); let s = "a\ b"; @@ -22,10 +23,10 @@ fn main() { b"; assert_eq!(s, "ab"); - let s = "a\ + let s = b"a\ b"; //~^^ WARNING whitespace symbol '\u{c}' is not skipped // '\x0c' is ASCII whitespace, but it may not need skipped // discussion: https://github.com/rust-lang/rust/pull/108403 - assert_eq!(s, "a\x0cb"); + assert_eq!(s, b"a\x0cb"); } diff --git a/tests/ui/str/str-escape.stderr b/tests/ui/str/str-escape.stderr index 43b4f7e36f6ab..00fe5444e1a4e 100644 --- a/tests/ui/str/str-escape.stderr +++ b/tests/ui/str/str-escape.stderr @@ -1,5 +1,5 @@ warning: multiple lines skipped by escaped newline - --> $DIR/str-escape.rs:5:14 + --> $DIR/str-escape.rs:6:14 | LL | let s = "\ | ______________^ @@ -8,20 +8,20 @@ LL | | "; | |_____________^ skipping everything up to and including this point warning: whitespace symbol '\u{a0}' is not skipped - --> $DIR/str-escape.rs:11:17 + --> $DIR/str-escape.rs:12:18 | -LL | let s = "foo\ - | _________________^ +LL | let s = c"foo\ + | __________________^ LL | |   bar | | ^ whitespace symbol '\u{a0}' is not skipped | |___| | warning: whitespace symbol '\u{c}' is not skipped - --> $DIR/str-escape.rs:25:15 + --> $DIR/str-escape.rs:26:16 | -LL | let s = "a\ - | _______________^ +LL | let s = b"a\ + | ________________^ LL | | b"; | | ^- whitespace symbol '\u{c}' is not skipped | |____|