]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/methods/mod.rs
get clippy to compile again
[rust.git] / clippy_lints / src / methods / mod.rs
index 26c29fbb289cb7f8d11e1aa41ce5d22d46a07cb0..9d4e1fa39940139b1c1248df21649db552b66cd7 100644 (file)
 mod iter_next_slice;
 mod iter_nth;
 mod iter_nth_zero;
+mod iter_overeager_cloned;
 mod iter_skip_next;
+mod iter_with_drain;
 mod iterator_step_by_zero;
 mod manual_saturating_arithmetic;
-mod manual_split_once;
 mod manual_str_repeat;
 mod map_collect_result_unit;
 mod map_flatten;
 mod option_map_or_none;
 mod option_map_unwrap_or;
 mod or_fun_call;
+mod or_then_unwrap;
 mod search_is_some;
 mod single_char_add_str;
 mod single_char_insert_string;
 mod single_char_pattern;
 mod single_char_push_string;
 mod skip_while_next;
+mod str_splitn;
 mod string_extend_chars;
 mod suspicious_map;
 mod suspicious_splitn;
 mod uninit_assumed_init;
 mod unnecessary_filter_map;
 mod unnecessary_fold;
+mod unnecessary_iter_cloned;
+mod unnecessary_join;
 mod unnecessary_lazy_eval;
+mod unnecessary_to_owned;
 mod unwrap_or_else_default;
 mod unwrap_used;
 mod useless_asref;
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
+use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, TraitRef, Ty, TyS};
+use rustc_middle::ty::{self, TraitRef, Ty};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::SymbolStr;
 use rustc_span::{sym, Span};
 use rustc_typeck::hir_ty_to_ty;
 
     /// ```rust
     /// [1, 2, 3].iter().copied();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub CLONED_INSTEAD_OF_COPIED,
     pedantic,
     "used `cloned` where `copied` could be used instead"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
+    ///
+    /// ### Why is this bad?
+    /// It's often inefficient to clone all elements of an iterator, when eventually, only some
+    /// of them will be consumed.
+    ///
+    /// ### Examples
+    /// ```rust
+    /// # let vec = vec!["string".to_string()];
+    ///
+    /// // Bad
+    /// vec.iter().cloned().take(10);
+    ///
+    /// // Good
+    /// vec.iter().take(10).cloned();
+    ///
+    /// // Bad
+    /// vec.iter().cloned().last();
+    ///
+    /// // Good
+    /// vec.iter().last().cloned();
+    ///
+    /// ```
+    /// ### Known Problems
+    /// This `lint` removes the side of effect of cloning items in the iterator.
+    /// A code that relies on that side-effect could fail.
+    ///
+    #[clippy::version = "1.59.0"]
+    pub ITER_OVEREAGER_CLONED,
+    perf,
+    "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
+}
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
     /// ```rust
     /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub FLAT_MAP_OPTION,
     pedantic,
     "used `flat_map` where `filter_map` could be used instead"
     /// // Good
     /// res.expect("more helpful message");
     /// ```
+    #[clippy::version = "1.45.0"]
     pub UNWRAP_USED,
     restriction,
     "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
     /// res?;
     /// # Ok::<(), ()>(())
     /// ```
+    #[clippy::version = "1.45.0"]
     pub EXPECT_USED,
     restriction,
     "using `.expect()` on `Result` or `Option`, which might be better handled"
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHOULD_IMPLEMENT_TRAIT,
     style,
     "defining a method that should be implementing a std trait"
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRONG_SELF_CONVENTION,
     style,
     "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
     /// // Good
     /// x.expect("why did I do this again?");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OK_EXPECT,
     style,
     "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
     /// // Good
     /// x.unwrap_or_default();
     /// ```
+    #[clippy::version = "1.56.0"]
     pub UNWRAP_OR_ELSE_DEFAULT,
     style,
     "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
     /// // Good
     /// x.map_or_else(some_function, |a| a + 1);
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MAP_UNWRAP_OR,
     pedantic,
     "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
     /// // Good
     /// opt.and_then(|a| Some(a + 1));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_MAP_OR_NONE,
     style,
     "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
     /// # let r: Result<u32, &str> = Ok(1);
     /// assert_eq!(Some(1), r.ok());
     /// ```
+    #[clippy::version = "1.44.0"]
     pub RESULT_MAP_OR_INTO_OPTION,
     style,
     "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
     /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
     /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
     /// ```
+    #[clippy::version = "1.45.0"]
     pub BIND_INSTEAD_OF_MAP,
     complexity,
     "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
     /// # let vec = vec![1];
     /// vec.iter().find(|x| **x == 0);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FILTER_NEXT,
     complexity,
     "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
     /// # let vec = vec![1];
     /// vec.iter().find(|x| **x != 0);
     /// ```
+    #[clippy::version = "1.42.0"]
     pub SKIP_WHILE_NEXT,
     complexity,
     "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
     ///
     /// ### Why is this bad?
     /// Readability, this can be written more concisely as
-    /// `_.flat_map(_)`
+    /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
     ///
     /// ### Example
     /// ```rust
     /// let vec = vec![vec![1]];
+    /// let opt = Some(5);
     ///
     /// // Bad
     /// vec.iter().map(|x| x.iter()).flatten();
+    /// opt.map(|x| Some(x * 2)).flatten();
     ///
     /// // Good
     /// vec.iter().flat_map(|x| x.iter());
+    /// opt.and_then(|x| Some(x * 2));
     /// ```
+    #[clippy::version = "1.31.0"]
     pub MAP_FLATTEN,
-    pedantic,
+    complexity,
     "using combinations of `flatten` and `map` which can usually be written as a single method call"
 }
 
     /// ```rust
     /// (0_i32..10).filter_map(|n| n.checked_add(1));
     /// ```
+    #[clippy::version = "1.51.0"]
     pub MANUAL_FILTER_MAP,
     complexity,
     "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
     /// ```rust
     /// (0_i32..10).find_map(|n| n.checked_add(1));
     /// ```
+    #[clippy::version = "1.51.0"]
     pub MANUAL_FIND_MAP,
     complexity,
     "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
     /// ```rust
     ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
     /// ```
+    #[clippy::version = "1.36.0"]
     pub FILTER_MAP_NEXT,
     pedantic,
     "using combination of `filter_map` and `next` which can usually be written as a single method call"
     /// # let iter = vec![vec![0]].into_iter();
     /// iter.flatten();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub FLAT_MAP_IDENTITY,
     complexity,
     "call to `flat_map` where `flatten` is sufficient"
     ///
     /// let _ = !"hello world".contains("world");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SEARCH_IS_SOME,
     complexity,
     "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
     /// let name = "foo";
     /// if name.starts_with('_') {};
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHARS_NEXT_CMP,
     style,
     "using `.chars().next()` to check if a string starts with a char"
     /// # let foo = Some(String::new());
     /// foo.unwrap_or_default();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OR_FUN_CALL,
     perf,
     "using any `*or` method with a function call, which suggests `*or_else`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `.or(…).unwrap()` calls to Options and Results.
+    ///
+    /// ### Why is this bad?
+    /// You should use `.unwrap_or(…)` instead for clarity.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let fallback = "fallback";
+    /// // Result
+    /// # type Error = &'static str;
+    /// # let result: Result<&str, Error> = Err("error");
+    /// let value = result.or::<Error>(Ok(fallback)).unwrap();
+    ///
+    /// // Option
+    /// # let option: Option<&str> = None;
+    /// let value = option.or(Some(fallback)).unwrap();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let fallback = "fallback";
+    /// // Result
+    /// # let result: Result<&str, &str> = Err("error");
+    /// let value = result.unwrap_or(fallback);
+    ///
+    /// // Option
+    /// # let option: Option<&str> = None;
+    /// let value = option.unwrap_or(fallback);
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub OR_THEN_UNWRAP,
+    complexity,
+    "checks for `.or(…).unwrap()` calls to Options and Results."
+}
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
     /// # let err_msg = "I'm a teapot";
     /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPECT_FUN_CALL,
     perf,
     "using any `expect` method with a function call"
     /// ```rust
     /// 42u64.clone();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_COPY,
     complexity,
     "using `clone` on a `Copy` type"
     /// // Good
     /// Rc::clone(&x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_REF_PTR,
     restriction,
     "using 'clone' on a ref-counted pointer"
     ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_DOUBLE_REF,
     correctness,
     "using `clone` on `&&T`"
     /// // OK, the specialized impl is used
     /// ["foo", "bar"].iter().map(|&s| s.to_string());
     /// ```
+    #[clippy::version = "1.40.0"]
     pub INEFFICIENT_TO_STRING,
     pedantic,
     "using `to_string` on `&&T` where `T: ToString`"
     ///     fn new() -> Self;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEW_RET_NO_SELF,
     style,
     "not returning type containing `Self` in a `new` method"
     ///
     /// // Good
     /// _.split('x');
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_CHAR_PATTERN,
     perf,
     "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
     ///     //..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITERATOR_STEP_BY_ZERO,
     correctness,
     "using `Iterator::step_by(0)`, which will panic at runtime"
     /// ```rust
     /// let _ = std::iter::empty::<Option<i32>>().flatten();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub OPTION_FILTER_MAP,
     complexity,
     "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
     /// # s.insert(1);
     /// let x = s.iter().next();
     /// ```
+    #[clippy::version = "1.42.0"]
     pub ITER_NTH_ZERO,
     style,
     "replace `iter.nth(0)` with `iter.next()`"
     /// let bad_vec = some_vec.get(3);
     /// let bad_slice = &some_vec[..].get(3);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_NTH,
     perf,
     "using `.iter().nth()` on a standard library type with O(1) element access"
     /// let bad_vec = some_vec.iter().nth(3);
     /// let bad_slice = &some_vec[..].iter().nth(3);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_SKIP_NEXT,
     style,
     "using `.skip(x).next()` on an iterator"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
+    ///
+    /// ### Why is this bad?
+    /// `.into_iter()` is simpler with better performance.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::collections::HashSet;
+    /// let mut foo = vec![0, 1, 2, 3];
+    /// let bar: HashSet<usize> = foo.drain(..).collect();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::collections::HashSet;
+    /// let foo = vec![0, 1, 2, 3];
+    /// let bar: HashSet<usize> = foo.into_iter().collect();
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub ITER_WITH_DRAIN,
+    nursery,
+    "replace `.drain(..)` with `.into_iter()`"
+}
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for use of `.get().unwrap()` (or
     /// let last = some_vec[3];
     /// some_vec[0] = 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub GET_UNWRAP,
     restriction,
     "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
     /// // Good
     /// a.append(&mut b);
     /// ```
+    #[clippy::version = "1.55.0"]
     pub EXTEND_WITH_DRAIN,
     perf,
     "using vec.append(&mut vec) to move the full range of a vecor to another"
     /// s.push_str(abc);
     /// s.push_str(&def);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_EXTEND_CHARS,
     style,
     "using `x.extend(s.chars())` where s is a `&str` or `String`"
     /// let s = [1, 2, 3, 4, 5];
     /// let s2: Vec<isize> = s.to_vec();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_CLONED_COLLECT,
     style,
     "using `.cloned().collect()` on slice to create a `Vec`"
     /// // Good
     /// name.ends_with('_') || name.ends_with('-');
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHARS_LAST_CMP,
     style,
     "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
     /// let x: &[i32] = &[1, 2, 3, 4, 5];
     /// do_stuff(x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_ASREF,
     complexity,
     "using `as_ref` where the types before and after the call are the same"
     /// ```rust
     /// let _ = (0..3).any(|x| x > 2);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_FOLD,
     style,
     "using `fold` when a more succinct alternative exists"
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `filter_map` calls which could be replaced by `filter` or `map`.
+    /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
     /// More specifically it checks if the closure provided is only performing one of the
     /// filter or map operations and suggests the appropriate option.
     ///
     /// // As there is no conditional check on the argument this could be written as:
     /// let _ = (0..4).map(|x| x + 1);
     /// ```
+    #[clippy::version = "1.31.0"]
     pub UNNECESSARY_FILTER_MAP,
     complexity,
     "using `filter_map` when a more succinct alternative exists"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `find_map` calls that could be replaced by `find` or `map`. More
+    /// specifically it checks if the closure provided is only performing one of the
+    /// find or map operations and suggests the appropriate option.
+    ///
+    /// ### Why is this bad?
+    /// Complexity. The intent is also clearer if only a single
+    /// operation is being performed.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
+    ///
+    /// // As there is no transformation of the argument this could be written as:
+    /// let _ = (0..3).find(|&x| x > 2);
+    /// ```
+    ///
+    /// ```rust
+    /// let _ = (0..4).find_map(|x| Some(x + 1));
+    ///
+    /// // As there is no conditional check on the argument this could be written as:
+    /// let _ = (0..4).map(|x| x + 1).next();
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub UNNECESSARY_FIND_MAP,
+    complexity,
+    "using `find_map` when a more succinct alternative exists"
+}
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for `into_iter` calls on references which should be replaced by `iter`
     /// // Good
     /// let _ = (&vec![3, 4, 5]).iter();
     /// ```
+    #[clippy::version = "1.32.0"]
     pub INTO_ITER_ON_REF,
     style,
     "using `.into_iter()` on a reference"
     /// ```rust
     /// let _ = (0..3).map(|x| x + 2).count();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub SUSPICIOUS_MAP,
     suspicious,
     "suspicious usage of map"
     ///     MaybeUninit::uninit().assume_init()
     /// };
     /// ```
+    #[clippy::version = "1.39.0"]
     pub UNINIT_ASSUMED_INIT,
     correctness,
     "`MaybeUninit::uninit().assume_init()`"
     /// let add = x.saturating_add(y);
     /// let sub = x.saturating_sub(y);
     /// ```
+    #[clippy::version = "1.39.0"]
     pub MANUAL_SATURATING_ARITHMETIC,
     style,
     "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
     /// ```rust
     /// unsafe { (&() as *const ()).offset(1) };
     /// ```
+    #[clippy::version = "1.41.0"]
     pub ZST_OFFSET,
     correctness,
     "Check for offset calculations on raw pointers to zero-sized types"
     /// # Ok::<_, std::io::Error>(())
     /// # };
     /// ```
+    #[clippy::version = "1.42.0"]
     pub FILETYPE_IS_FILE,
     restriction,
     "`FileType::is_file` is not recommended to test for readable file type"
     /// opt.as_deref()
     /// # ;
     /// ```
+    #[clippy::version = "1.42.0"]
     pub OPTION_AS_REF_DEREF,
     complexity,
     "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
     /// a.get(2);
     /// b.get(0);
     /// ```
+    #[clippy::version = "1.46.0"]
     pub ITER_NEXT_SLICE,
     style,
     "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
     /// string.insert(0, 'R');
     /// string.push('R');
     /// ```
+    #[clippy::version = "1.49.0"]
     pub SINGLE_CHAR_ADD_STR,
     style,
     "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
     ///
     /// opt.unwrap_or(42);
     /// ```
+    #[clippy::version = "1.48.0"]
     pub UNNECESSARY_LAZY_EVALUATIONS,
     style,
     "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
     /// ```rust
     /// (0..3).try_for_each(|t| Err(t));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MAP_COLLECT_RESULT_UNIT,
     style,
     "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
     ///
     /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub FROM_ITER_INSTEAD_OF_COLLECT,
     pedantic,
     "use `.collect()` instead of `::from_iter()`"
     ///     assert!(x >= 0);
     /// });
     /// ```
+    #[clippy::version = "1.51.0"]
     pub INSPECT_FOR_EACH,
     complexity,
     "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
     /// # let iter = vec![Some(1)].into_iter();
     /// iter.flatten();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub FILTER_MAP_IDENTITY,
     complexity,
     "call to `filter_map` where `flatten` is sufficient"
     /// let x = [1, 2, 3];
     /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MAP_IDENTITY,
     complexity,
     "using iterator.map(|x| x)"
     /// // Good
     /// let _ = "Hello".as_bytes().get(3);
     /// ```
+    #[clippy::version = "1.52.0"]
     pub BYTES_NTH,
     style,
     "replace `.bytes().nth()` with `.as_bytes().get()`"
     /// let b = a.clone();
     /// let c = a.clone();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub IMPLICIT_CLONE,
     pedantic,
     "implicitly cloning a value by invoking a function on its dereferenced type"
     /// let _ = some_vec.len();
     /// let _ = &some_vec[..].len();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub ITER_COUNT,
     complexity,
     "replace `.iter().count()` with `.len()`"
     ///     // use x
     /// }
     /// ```
+    #[clippy::version = "1.54.0"]
     pub SUSPICIOUS_SPLITN,
     correctness,
     "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
     /// // Good
     /// let x: String = "x".repeat(10);
     /// ```
+    #[clippy::version = "1.54.0"]
     pub MANUAL_STR_REPEAT,
     perf,
     "manual implementation of `str::repeat`"
     /// let (key, value) = _.split_once('=')?;
     /// let value = _.split_once('=')?.1;
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MANUAL_SPLIT_ONCE,
     complexity,
     "replace `.splitn(2, pat)` with `.split_once(pat)`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
+    /// ### Why is this bad?
+    /// The function `split` is simpler and there is no performance difference in these cases, considering
+    /// that both functions return a lazy iterator.
+    /// ### Example
+    /// ```rust
+    /// // Bad
+    /// let str = "key=value=add";
+    /// let _ = str.splitn(3, '=').next().unwrap();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// // Good
+    /// let str = "key=value=add";
+    /// let _ = str.split('=').next().unwrap();
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub NEEDLESS_SPLITN,
+    complexity,
+    "usages of `str::splitn` that can be replaced with `str::split`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
+    /// and other `to_owned`-like functions.
+    ///
+    /// ### Why is this bad?
+    /// The unnecessary calls result in useless allocations.
+    ///
+    /// ### Known problems
+    /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
+    /// owned copy of a resource and the resource is later used mutably. See
+    /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
+    ///
+    /// ### Example
+    /// ```rust
+    /// let path = std::path::Path::new("x");
+    /// foo(&path.to_string_lossy().to_string());
+    /// fn foo(s: &str) {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let path = std::path::Path::new("x");
+    /// foo(&path.to_string_lossy());
+    /// fn foo(s: &str) {}
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub UNNECESSARY_TO_OWNED,
+    perf,
+    "unnecessary calls to `to_owned`-like functions"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
+    ///
+    /// ### Why is this bad?
+    /// `.collect::<String>()` is more concise and usually more performant
+    ///
+    /// ### Example
+    /// ```rust
+    /// let vector = vec!["hello",  "world"];
+    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
+    /// println!("{}", output);
+    /// ```
+    /// The correct use would be:
+    /// ```rust
+    /// let vector = vec!["hello",  "world"];
+    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
+    /// println!("{}", output);
+    /// ```
+    /// ### Known problems
+    /// While `.collect::<String>()` is more performant in most cases, there are cases where
+    /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
+    /// will prevent loop unrolling and will result in a negative performance impact.
+    #[clippy::version = "1.61.0"]
+    pub UNNECESSARY_JOIN,
+    pedantic,
+    "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -1825,12 +2106,14 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
     OPTION_MAP_OR_NONE,
     BIND_INSTEAD_OF_MAP,
     OR_FUN_CALL,
+    OR_THEN_UNWRAP,
     EXPECT_FUN_CALL,
     CHARS_NEXT_CMP,
     CHARS_LAST_CMP,
     CLONE_ON_COPY,
     CLONE_ON_REF_PTR,
     CLONE_DOUBLE_REF,
+    ITER_OVEREAGER_CLONED,
     CLONED_INSTEAD_OF_COPIED,
     FLAT_MAP_OPTION,
     INEFFICIENT_TO_STRING,
@@ -1858,9 +2141,11 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
     GET_UNWRAP,
     STRING_EXTEND_CHARS,
     ITER_CLONED_COLLECT,
+    ITER_WITH_DRAIN,
     USELESS_ASREF,
     UNNECESSARY_FOLD,
     UNNECESSARY_FILTER_MAP,
+    UNNECESSARY_FIND_MAP,
     INTO_ITER_ON_REF,
     SUSPICIOUS_MAP,
     UNINIT_ASSUMED_INIT,
@@ -1876,31 +2161,26 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
     SUSPICIOUS_SPLITN,
     MANUAL_STR_REPEAT,
     EXTEND_WITH_DRAIN,
-    MANUAL_SPLIT_ONCE
+    MANUAL_SPLIT_ONCE,
+    NEEDLESS_SPLITN,
+    UNNECESSARY_TO_OWNED,
+    UNNECESSARY_JOIN,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
-fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(SymbolStr, &'tcx [hir::Expr<'tcx>], Span)> {
-    if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
+fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
+    if let ExprKind::MethodCall(path, args, _) = recv.kind {
         if !args.iter().any(|e| e.span.from_expansion()) {
-            return Some((path.ident.name.as_str(), args, span));
+            let name = path.ident.name.as_str();
+            return Some((name, args, path.ident.span));
         }
     }
     None
 }
 
-/// Same as `method_call` but the `SymbolStr` is dereferenced into a temporary `&str`
-macro_rules! method_call {
-    ($expr:expr) => {
-        method_call($expr)
-            .as_ref()
-            .map(|&(ref name, args, span)| (&**name, args, span))
-    };
-}
-
 impl<'tcx> LateLintPass<'tcx> for Methods {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
@@ -1910,15 +2190,17 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             hir::ExprKind::Call(func, args) => {
                 from_iter_instead_of_collect::check(cx, expr, args, func);
             },
-            hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
-                or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
-                expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
+            hir::ExprKind::MethodCall(method_call, args, _) => {
+                let method_span = method_call.ident.span;
+                or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
+                expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
                 clone_on_copy::check(cx, expr, method_call.ident.name, args);
                 clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
                 inefficient_to_string::check(cx, expr, method_call.ident.name, args);
                 single_char_add_str::check(cx, expr, args);
-                into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
+                into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
                 single_char_pattern::check(cx, expr, method_call.ident.name, args);
+                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
             },
             hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
                 let mut info = BinaryExprInfo {
@@ -1951,7 +2233,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
             let method_sig = cx.tcx.fn_sig(impl_item.def_id);
             let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 
-            let first_arg_ty = &method_sig.inputs().iter().next();
+            let first_arg_ty = method_sig.inputs().iter().next();
 
             // check conventions w.r.t. conversion method names and predicates
             if let Some(first_arg_ty) = first_arg_ty;
@@ -1964,7 +2246,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
                         if name == method_config.method_name &&
                             sig.decl.inputs.len() == method_config.param_count &&
                             method_config.output_type.matches(&sig.decl.output) &&
-                            method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
+                            method_config.self_kind.matches(cx, self_ty, *first_arg_ty) &&
                             fn_header_equals(method_config.fn_header, sig.header) &&
                             method_config.lifetime_param_cond(impl_item)
                         {
@@ -1994,9 +2276,9 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
                 {
                     wrong_self_convention::check(
                         cx,
-                        &name,
+                        name,
                         self_ty,
-                        first_arg_ty,
+                        *first_arg_ty,
                         first_arg.pat.span,
                         implements_trait,
                         false
@@ -2015,10 +2297,10 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
 
             // walk the return type and check for Self (this does not check associated types)
             if let Some(self_adt) = self_ty.ty_adt_def() {
-                if contains_adt_constructor(cx.tcx, ret_ty, self_adt) {
+                if contains_adt_constructor(ret_ty, self_adt) {
                     return;
                 }
-            } else if contains_ty(cx.tcx, ret_ty, self_ty) {
+            } else if contains_ty(ret_ty, self_ty) {
                 return;
             }
 
@@ -2027,19 +2309,23 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
                 // one of the associated types must be Self
                 for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
                     if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
+                        let assoc_ty = match projection_predicate.term {
+                            ty::Term::Ty(ty) => ty,
+                            ty::Term::Const(_c) => continue,
+                        };
                         // walk the associated type and check for Self
                         if let Some(self_adt) = self_ty.ty_adt_def() {
-                            if contains_adt_constructor(cx.tcx, projection_predicate.ty, self_adt) {
+                            if contains_adt_constructor(assoc_ty, self_adt) {
                                 return;
                             }
-                        } else if contains_ty(cx.tcx, projection_predicate.ty, self_ty) {
+                        } else if contains_ty(assoc_ty, self_ty) {
                             return;
                         }
                     }
                 }
             }
 
-            if name == "new" && !TyS::same_type(ret_ty, self_ty) {
+            if name == "new" && ret_ty != self_ty {
                 span_lint(
                     cx,
                     NEW_RET_NO_SELF,
@@ -2066,7 +2352,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
                 let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
                 wrong_self_convention::check(
                     cx,
-                    &item.ident.name.as_str(),
+                    item.ident.name.as_str(),
                     self_ty,
                     first_arg_ty,
                     first_arg_span,
@@ -2081,7 +2367,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
             if let TraitItemKind::Fn(_, _) = item.kind;
             let ret_ty = return_ty(cx, item.hir_id());
             let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
-            if !contains_ty(cx.tcx, ret_ty, self_ty);
+            if !contains_ty(ret_ty, self_ty);
 
             then {
                 span_lint(
@@ -2099,7 +2385,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
 
 #[allow(clippy::too_many_lines)]
 fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
-    if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
+    if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
         match (name, args) {
             ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
                 zst_offset::check(cx, expr, recv);
@@ -2115,8 +2401,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
             ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
-            ("collect", []) => match method_call!(recv) {
-                Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
+            ("collect", []) => match method_call(recv) {
+                Some((name @ ("cloned" | "copied"), [recv2], _)) => {
+                    iter_cloned_collect::check(cx, name, expr, recv2);
+                },
                 Some(("map", [m_recv, m_arg], _)) => {
                     map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
                 },
@@ -2127,14 +2415,18 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 },
                 _ => {},
             },
-            ("count", []) => match method_call!(recv) {
-                Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
-                    iter_count::check(cx, expr, recv2, name);
+            (name @ "count", args @ []) => match method_call(recv) {
+                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+                Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
+                    iter_count::check(cx, expr, recv2, name2);
                 },
                 Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
                 _ => {},
             },
-            ("expect", [_]) => match method_call!(recv) {
+            ("drain", [arg]) => {
+                iter_with_drain::check(cx, expr, recv, span, arg);
+            },
+            ("expect", [_]) => match method_call(recv) {
                 Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
                 _ => expect_used::check(cx, expr, recv),
             },
@@ -2143,21 +2435,24 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 extend_with_drain::check(cx, expr, recv, arg);
             },
             ("filter_map", [arg]) => {
-                unnecessary_filter_map::check(cx, expr, arg);
+                unnecessary_filter_map::check(cx, expr, arg, name);
                 filter_map_identity::check(cx, expr, arg, span);
             },
+            ("find_map", [arg]) => {
+                unnecessary_filter_map::check(cx, expr, arg, name);
+            },
             ("flat_map", [arg]) => {
                 flat_map_identity::check(cx, expr, arg, span);
                 flat_map_option::check(cx, expr, arg, span);
             },
-            ("flatten", []) => {
-                if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
-                    map_flatten::check(cx, expr, recv, map_arg);
-                }
+            (name @ "flatten", args @ []) => match method_call(recv) {
+                Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
+                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+                _ => {},
             },
             ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
             ("for_each", [_]) => {
-                if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
+                if let Some(("inspect", [_, _], span2)) = method_call(recv) {
                     inspect_for_each::check(cx, expr, span2);
                 }
             },
@@ -2165,8 +2460,20 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("is_file", []) => filetype_is_file::check(cx, expr, recv),
             ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
             ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+            ("join", [join_arg]) => {
+                if let Some(("collect", _, span)) = method_call(recv) {
+                    unnecessary_join::check(cx, expr, recv, join_arg, span);
+                }
+            },
+            ("last", args @ []) | ("skip", args @ [_]) => {
+                if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+                    if let ("cloned", []) = (name2, args2) {
+                        iter_overeager_cloned::check(cx, expr, recv2, name, args);
+                    }
+                }
+            },
             ("map", [m_arg]) => {
-                if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
+                if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
                     match (name, args) {
                         ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
                         ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
@@ -2180,20 +2487,22 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 map_identity::check(cx, expr, recv, m_arg, span);
             },
             ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
-            ("next", []) => {
-                if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
-                    match (name, args) {
-                        ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
-                        ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
-                        ("iter", []) => iter_next_slice::check(cx, expr, recv),
-                        ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg),
+            (name @ "next", args @ []) => {
+                if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
+                    match (name2, args2) {
+                        ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+                        ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
+                        ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, msrv),
+                        ("iter", []) => iter_next_slice::check(cx, expr, recv2),
+                        ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
                         ("skip_while", [_]) => skip_while_next::check(cx, expr),
                         _ => {},
                     }
                 }
             },
-            ("nth", [n_arg]) => match method_call!(recv) {
+            ("nth", args @ [n_arg]) => match method_call(recv) {
                 Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
+                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
                 Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
                 Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
                 _ => iter_nth_zero::check(cx, expr, recv, n_arg),
@@ -2208,7 +2517,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
                     suspicious_splitn::check(cx, name, expr, recv, count);
                     if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
-                        manual_split_once::check(cx, name, expr, recv, pat_arg);
+                        str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
+                    }
+                    if count >= 2 {
+                        str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
                     }
                 }
             },
@@ -2218,15 +2530,32 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 }
             },
             ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
+            ("take", args @ [_arg]) => {
+                if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+                    if let ("cloned", []) = (name2, args2) {
+                        iter_overeager_cloned::check(cx, expr, recv2, name, args);
+                    }
+                }
+            },
             ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
-                implicit_clone::check(cx, name, expr, recv, span);
+                implicit_clone::check(cx, name, expr, recv);
             },
-            ("unwrap", []) => match method_call!(recv) {
-                Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
-                Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
-                _ => unwrap_used::check(cx, expr, recv),
+            ("unwrap", []) => {
+                match method_call(recv) {
+                    Some(("get", [recv, get_arg], _)) => {
+                        get_unwrap::check(cx, expr, recv, get_arg, false);
+                    },
+                    Some(("get_mut", [recv, get_arg], _)) => {
+                        get_unwrap::check(cx, expr, recv, get_arg, true);
+                    },
+                    Some(("or", [recv, or_arg], or_span)) => {
+                        or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
+                    },
+                    _ => {},
+                }
+                unwrap_used::check(cx, expr, recv);
             },
-            ("unwrap_or", [u_arg]) => match method_call!(recv) {
+            ("unwrap_or", [u_arg]) => match method_call(recv) {
                 Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
                     manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
                 },
@@ -2235,7 +2564,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 },
                 _ => {},
             },
-            ("unwrap_or_else", [u_arg]) => match method_call!(recv) {
+            ("unwrap_or_else", [u_arg]) => match method_call(recv) {
                 Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
                 _ => {
                     unwrap_or_else_default::check(cx, expr, recv, u_arg);
@@ -2248,7 +2577,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
 }
 
 fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
-    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
+    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
         search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
     }
 }
@@ -2412,11 +2741,17 @@ fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty:
             implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
         }
 
+        fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+            !matches_value(cx, parent_ty, ty)
+                && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
+                && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
+        }
+
         match self {
             Self::Value => matches_value(cx, parent_ty, ty),
             Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
             Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
-            Self::No => ty != parent_ty,
+            Self::No => matches_none(cx, parent_ty, ty),
         }
     }