about summary refs log tree commit diff
path: root/clippy_lints/src/methods/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints/src/methods/mod.rs')
-rw-r--r--clippy_lints/src/methods/mod.rs107
1 files changed, 100 insertions, 7 deletions
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index e46739fea34..58ec2213535 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -33,7 +33,6 @@ mod iter_nth_zero;
 mod iter_skip_next;
 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;
@@ -50,6 +49,7 @@ 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;
@@ -68,7 +68,7 @@ use bind_instead_of_map::BindInsteadOfMap;
 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;
@@ -99,6 +99,7 @@ declare_clippy_lint! {
     /// ```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"
@@ -121,6 +122,7 @@ declare_clippy_lint! {
     /// ```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"
@@ -166,6 +168,7 @@ declare_clippy_lint! {
     /// // 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()`"
@@ -208,6 +211,7 @@ declare_clippy_lint! {
     /// res?;
     /// # Ok::<(), ()>(())
     /// ```
+    #[clippy::version = "1.45.0"]
     pub EXPECT_USED,
     restriction,
     "using `.expect()` on `Result` or `Option`, which might be better handled"
@@ -237,6 +241,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHOULD_IMPLEMENT_TRAIT,
     style,
     "defining a method that should be implementing a std trait"
@@ -284,6 +289,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[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"
@@ -310,6 +316,7 @@ declare_clippy_lint! {
     /// // 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"
@@ -335,6 +342,7 @@ declare_clippy_lint! {
     /// // 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()`"
@@ -375,6 +383,7 @@ declare_clippy_lint! {
     /// // 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)`"
@@ -401,6 +410,7 @@ declare_clippy_lint! {
     /// // 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)`"
@@ -426,6 +436,7 @@ declare_clippy_lint! {
     /// # 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()`"
@@ -458,6 +469,7 @@ declare_clippy_lint! {
     /// 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)`"
@@ -481,6 +493,7 @@ declare_clippy_lint! {
     /// # 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)`"
@@ -504,6 +517,7 @@ declare_clippy_lint! {
     /// # 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)`"
@@ -527,8 +541,9 @@ declare_clippy_lint! {
     /// // Good
     /// vec.iter().flat_map(|x| x.iter());
     /// ```
+    #[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"
 }
 
@@ -553,6 +568,7 @@ declare_clippy_lint! {
     /// ```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(_)`"
@@ -579,6 +595,7 @@ declare_clippy_lint! {
     /// ```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(_)`"
@@ -601,6 +618,7 @@ declare_clippy_lint! {
     /// ```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"
@@ -623,6 +641,7 @@ declare_clippy_lint! {
     /// # 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"
@@ -652,6 +671,7 @@ declare_clippy_lint! {
     ///
     /// 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()`)"
@@ -676,6 +696,7 @@ declare_clippy_lint! {
     /// 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"
@@ -710,6 +731,7 @@ declare_clippy_lint! {
     /// # 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`"
@@ -748,6 +770,7 @@ declare_clippy_lint! {
     /// # 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"
@@ -765,6 +788,7 @@ declare_clippy_lint! {
     /// ```rust
     /// 42u64.clone();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_COPY,
     complexity,
     "using `clone` on a `Copy` type"
@@ -792,6 +816,7 @@ declare_clippy_lint! {
     /// // Good
     /// Rc::clone(&x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_REF_PTR,
     restriction,
     "using 'clone' on a ref-counted pointer"
@@ -814,6 +839,7 @@ declare_clippy_lint! {
     ///     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`"
@@ -837,6 +863,7 @@ declare_clippy_lint! {
     /// // 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`"
@@ -898,6 +925,7 @@ declare_clippy_lint! {
     ///     fn new() -> Self;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEW_RET_NO_SELF,
     style,
     "not returning type containing `Self` in a `new` method"
@@ -922,6 +950,7 @@ declare_clippy_lint! {
     ///
     /// // 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\")`"
@@ -941,6 +970,7 @@ declare_clippy_lint! {
     ///     //..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITERATOR_STEP_BY_ZERO,
     correctness,
     "using `Iterator::step_by(0)`, which will panic at runtime"
@@ -962,6 +992,7 @@ declare_clippy_lint! {
     /// ```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"
@@ -989,6 +1020,7 @@ declare_clippy_lint! {
     /// # 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()`"
@@ -1015,6 +1047,7 @@ declare_clippy_lint! {
     /// 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"
@@ -1039,6 +1072,7 @@ declare_clippy_lint! {
     /// 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"
@@ -1075,6 +1109,7 @@ declare_clippy_lint! {
     /// 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"
@@ -1098,6 +1133,7 @@ declare_clippy_lint! {
     /// // 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"
@@ -1127,6 +1163,7 @@ declare_clippy_lint! {
     /// 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`"
@@ -1150,6 +1187,7 @@ declare_clippy_lint! {
     /// 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`"
@@ -1174,6 +1212,7 @@ declare_clippy_lint! {
     /// // 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"
@@ -1199,6 +1238,7 @@ declare_clippy_lint! {
     /// 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"
@@ -1221,6 +1261,7 @@ declare_clippy_lint! {
     /// ```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"
@@ -1250,6 +1291,7 @@ declare_clippy_lint! {
     /// // 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"
@@ -1273,6 +1315,7 @@ declare_clippy_lint! {
     /// // Good
     /// let _ = (&vec![3, 4, 5]).iter();
     /// ```
+    #[clippy::version = "1.32.0"]
     pub INTO_ITER_ON_REF,
     style,
     "using `.into_iter()` on a reference"
@@ -1292,6 +1335,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = (0..3).map(|x| x + 2).count();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub SUSPICIOUS_MAP,
     suspicious,
     "suspicious usage of map"
@@ -1326,6 +1370,7 @@ declare_clippy_lint! {
     ///     MaybeUninit::uninit().assume_init()
     /// };
     /// ```
+    #[clippy::version = "1.39.0"]
     pub UNINIT_ASSUMED_INIT,
     correctness,
     "`MaybeUninit::uninit().assume_init()`"
@@ -1354,6 +1399,7 @@ declare_clippy_lint! {
     /// 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)`"
@@ -1371,6 +1417,7 @@ declare_clippy_lint! {
     /// ```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"
@@ -1412,6 +1459,7 @@ declare_clippy_lint! {
     /// # 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"
@@ -1437,6 +1485,7 @@ declare_clippy_lint! {
     /// 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()`"
@@ -1463,6 +1512,7 @@ declare_clippy_lint! {
     /// 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()`"
@@ -1488,6 +1538,7 @@ declare_clippy_lint! {
     /// 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"
@@ -1526,6 +1577,7 @@ declare_clippy_lint! {
     ///
     /// 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"
@@ -1546,6 +1598,7 @@ declare_clippy_lint! {
     /// ```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`"
@@ -1578,6 +1631,7 @@ declare_clippy_lint! {
     ///
     /// 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()`"
@@ -1607,6 +1661,7 @@ declare_clippy_lint! {
     ///     assert!(x >= 0);
     /// });
     /// ```
+    #[clippy::version = "1.51.0"]
     pub INSPECT_FOR_EACH,
     complexity,
     "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
@@ -1629,6 +1684,7 @@ declare_clippy_lint! {
     /// # 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"
@@ -1651,6 +1707,7 @@ declare_clippy_lint! {
     /// 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)"
@@ -1672,6 +1729,7 @@ declare_clippy_lint! {
     /// // Good
     /// let _ = "Hello".as_bytes().get(3);
     /// ```
+    #[clippy::version = "1.52.0"]
     pub BYTES_NTH,
     style,
     "replace `.bytes().nth()` with `.as_bytes().get()`"
@@ -1697,6 +1755,7 @@ declare_clippy_lint! {
     /// 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"
@@ -1722,6 +1781,7 @@ declare_clippy_lint! {
     /// let _ = some_vec.len();
     /// let _ = &some_vec[..].len();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub ITER_COUNT,
     complexity,
     "replace `.iter().count()` with `.len()`"
@@ -1751,6 +1811,7 @@ declare_clippy_lint! {
     ///     // use x
     /// }
     /// ```
+    #[clippy::version = "1.54.0"]
     pub SUSPICIOUS_SPLITN,
     correctness,
     "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
@@ -1771,6 +1832,7 @@ declare_clippy_lint! {
     /// // Good
     /// let x: String = "x".repeat(10);
     /// ```
+    #[clippy::version = "1.54.0"]
     pub MANUAL_STR_REPEAT,
     perf,
     "manual implementation of `str::repeat`"
@@ -1793,11 +1855,36 @@ declare_clippy_lint! {
     /// 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`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -1876,7 +1963,8 @@ impl_lint_pass!(Methods => [
     SUSPICIOUS_SPLITN,
     MANUAL_STR_REPEAT,
     EXTEND_WITH_DRAIN,
-    MANUAL_SPLIT_ONCE
+    MANUAL_SPLIT_ONCE,
+    NEEDLESS_SPLITN
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -1900,7 +1988,7 @@ macro_rules! method_call {
 
 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;
         }
 
@@ -2116,7 +2204,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("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),
+                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);
                 },
@@ -2208,7 +2298,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);
                     }
                 }
             },